17
Two-Pane Master-Detail Interfaces

In this chapter, you will create a tablet interface for CriminalIntent that allows users to see and interact with the list of crimes and the details of an individual crime at the same time. Figure 17.1 shows this list-detail interface, which is also commonly referred to as a master-detail interface.

Figure 17.1  Master and detail sharing the spotlight

Screenshot shows CriminalIntent app in Android.

You will need a tablet device or AVD for testing in this chapter. To create a tablet AVD, select ToolsAndroidAVD Manager. Click + Create Virtual Device... and select the Tablet category on the left. Select your favorite hardware profile (Figure 17.2), click Next, and choose an API level of at least 21.

Figure 17.2  Device selections for a tablet AVD

Figure shows Phone and Tablet layout.

Adding Layout Flexibility

On a phone, you want CrimeListActivity to inflate a single-pane layout, as it currently does. On a tablet, you want it to inflate a two-pane layout that is capable of displaying the master and detail views at the same time.

In the two-pane layout, CrimeListActivity will host both a CrimeListFragment and a CrimeFragment, as shown in Figure 17.3.

Figure 17.3  Different types of layouts

Figure shows Phone and Tablet layout.

To make this happen, you are going to:

  • modify SingleFragmentActivity so that the layout that gets inflated is not hardcoded

  • create a new layout that consists of two fragment containers

  • modify CrimeListActivity so that it will inflate a single-container layout on phones and a two-container layout on tablets

Modifying SingleFragmentActivity

CrimeListActivity is a subclass of SingleFragmentActivity. Currently, SingleFragmentActivity is set up to always inflate activity_fragment.xml. To make SingleFragmentActivity more flexible, you are going to enable a subclass to provide its own resource ID for the layout instead.

In SingleFragmentActivity.java, add a protected method that returns the ID of the layout that the activity will inflate.

Listing 17.1  Making SingleFragmentActivity flexible (SingleFragmentActivity.java)

public abstract class SingleFragmentActivity extends AppCompatActivity {

    protected abstract Fragment createFragment();

    @LayoutRes
    protected int getLayoutResId() {
        return R.layout.activity_fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment);
        setContentView(getLayoutResId());

        FragmentManager fm = getSupportFragmentManager();
        ...
    }
}

The default implementation of SingleFragmentActivity will work the same as before, but now its subclasses can choose to override getLayoutResId() to return a layout other than activity_fragment.xml. You annotate getLayoutResId() with @LayoutRes to tell Android Studio that any implementation of this method should return a valid layout resource ID.

Creating a layout with two fragment containers

In the project tool window, right-click res/layout/ and create a new Android XML file. Ensure that the resource type is Layout, name the file activity_twopane.xml, and give it a LinearLayout root element.

Use Figure 17.4 to write the XML for the two-pane layout.

Figure 17.4  A layout with two fragment containers (layout/activity_twopane.xml)

Figure shows Title Layout.

Note that the first FrameLayout has a fragment_container layout ID, so the code in SingleFragmentActivity.onCreate(…) can work as before. When the activity is created, the fragment that is returned in createFragment() will appear in the lefthand pane.

Test this layout in CrimeListActivity by overriding getLayoutResId() to return R.layout.activity_twopane.

Listing 17.2  Changing to two-pane layout file (CrimeListActivity.java)

public class CrimeListActivity extends SingleFragmentActivity {

    @Override
    protected Fragment createFragment() {
        return new CrimeListFragment();
    }

    @Override
    protected int getLayoutResId() {
        return R.layout.activity_twopane;
    }
}

Run CriminalIntent on a tablet device and confirm that you have two panes (Figure 17.5). (You will need to add a crime to see the panes.) Note that the larger detail pane is empty and that pressing a list item will not display the crime’s details. You will hook up the detail container later in the chapter.

Figure 17.5  Two-pane layout on a tablet

Screenshot shows the CriminalIntent app in Android. The left side of the screen shows a list of crimes.

As currently written, CrimeListActivity will also inflate the two-pane interface when running on a phone. In the next section, you will fix that using an alias resource.

Using an alias resource

An alias resource is a resource that points to another resource. Alias resources live in res/values/ and, by convention, are defined in a refs.xml file.

Your next job will be to have CrimeListActivity show a different layout file depending on whether it is on a tablet or a phone. You do this the same way you show a different layout for landscape and portrait: by using a resource qualifier.

Doing that with files in res/layout works, but it has some drawbacks. Each layout file has to contain a complete copy of the layout you want to show. This can result in a lot of redundancy. If you wanted an activity_masterdetail.xml layout file, you would have to copy all of activity_fragment.xml into res/layout/activity_masterdetail.xml and all of activity_twopane.xml into res/layout-sw600dp/activity_masterdetail.xml. (You will see what sw600dp does in a moment.)

Instead of doing that, you will use an alias resource. In this section, you will create an alias resource that points to the activity_fragment.xml layout on phones and the activity_twopane.xml layout on tablets.

In the project tool window, right-click the res/values directory and create a new values resource file. Name the file refs.xml and check that the directory is values. It should have no qualifiers. Click OK. Then add the item shown in Listing 17.3.

Listing 17.3  Creating a default alias resource value (res/values/refs.xml)

<resources>

  <item name="activity_masterdetail" type="layout">@layout/activity_fragment</item>

</resources>

This resource’s value is a reference to the single-pane layout. It also has a resource ID: R.layout.activity_masterdetail. Note that the alias’s type attribute is what determines the inner class of the ID. Even though the alias itself is in res/values/, its ID is in R.layout.

You can now use this resource ID in place of R.layout.activity_fragment. Make that change in CrimeListActivity.

Listing 17.4  Switching layout again (CrimeListActivity.java)

@Override
protected int getLayoutResId() {
    return R.layout.activity_twopane;masterdetail;
}

Run CriminalIntent to confirm that your alias is working properly. CrimeListActivity should inflate the single-pane layout again.

Creating tablet alternatives

Because your alias is in res/values/, it is the default alias. So, by default, CrimeListActivity inflates the single-pane layout.

Now you are going to create an alternative resource so that the activity_masterdetail alias will point to activity_twopane.xml on larger devices.

In the project tool window, right-click res/values/ and create a new values resource file. As before, name the file refs.xml and check that its directory is values. But this time, select Smallest Screen Width under Available qualifiers and click the >> button to move it over to the right (Figure 17.6).

Figure 17.6  Adding a qualifier

Screenshot shows adding a qualifier in New Resource file window.

This qualifier is a bit different. It asks you to specify a value for the smallest screen width. Enter 600 here, and click OK. Once your new resource file opens, add the activity_masterdetail alias to this file, too, pointing at a different layout file.

Listing 17.5  Alternative alias for larger devices (res/values-sw600dp/refs.xml)

<resources>

  <item name="activity_masterdetail" type="layout">@layout/activity_twopane</item>

</resources>

Let’s explain what you are doing here. Your goal is to have logic that works like this:

  • For devices that are under a specified size, use activity_fragment.xml.

  • For devices that are over a specified size, use activity_twopane.xml.

Android does not provide a way to use a resource only when a device is under a particular size, but it does provide the next best thing. The -sw600dp configuration qualifier lets you provide resources only when a device is above a certain size. The sw stands for smallest width, but refers to the screen’s smallest dimension, and thus is independent of the device’s current orientation.

With a -sw600dp qualifier, you are saying, Use this resource on any device whose smallest dimension is 600dp or greater. This is a good rule of thumb for specifying a tablet-sized screen.

What about the other part, where you want to use activity_fragment.xml on smaller devices? Smaller devices will not match your -sw600dp resource, so the default will be used: activity_fragment.xml.

Run CriminalIntent on a phone and on a tablet. Confirm that the single- and two-pane layouts appear where you expect them.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.40.207