11
Using ViewPager

In this chapter, you will create a new activity to host CrimeFragment. This activity’s layout will consist of an instance of ViewPager. Adding a ViewPager to your UI lets users navigate between list items by swiping across the screen to page forward or backward through the crimes (Figure 11.1).

Figure 11.1  Swiping to page through crimes

Illustration shows Swiping to page through crimes.

Figure 11.2 shows an updated diagram for CriminalIntent. The new activity will be named CrimePagerActivity and will take the place of CrimeActivity. Its layout will consist of a ViewPager.

Figure 11.2  Object diagram for CrimePagerActivity

Figure shows Object diagram for CrimePagerActivity.

The only new objects you need to create are within the dashed rectangle in the object diagram: CrimePagerActivity and ViewPager. Nothing else in CriminalIntent needs to change to implement paging between detail views. In particular, you will not have to touch the CrimeFragment class, thanks to the work you did in Chapter 10 to ensure CrimeFragment’s independence.

Here are the tasks ahead in this chapter:

  • create the CrimePagerActivity class

  • define a view hierarchy that consists of a ViewPager

  • wire up the ViewPager and its adapter in CrimePagerActivity

  • modify CrimeHolder.onClick(…) to start CrimePagerActivity instead of CrimeActivity

Creating CrimePagerActivity

CrimePagerActivity will be a subclass of AppCompatActivity. It will create and manage the ViewPager.

Create a new class named CrimePagerActivity. Make its superclass AppCompatActivity and set up the view for the activity.

Listing 11.1  Setting up ViewPager (CrimePagerActivity.java)

public class CrimePagerActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_crime_pager);
    }
}

The layout file does not yet exist. Create a new layout file in res/layout/ and name it activity_crime_pager. Make its root view a ViewPager and give it the attributes shown in Figure 11.3. Notice that you must use ViewPager’s full package name (android.support.v4.view.ViewPager).

Figure 11.3  CrimePagerActivity’s ViewPager (activity_crime_pager.xml)

Figure shows CrimePagerActivity's ViewPager.

You use ViewPager’s full package name when adding it to the layout file because the ViewPager class is from the support library. Unlike Fragment, ViewPager is only available in the support library; there is not a standard ViewPager class in a later SDK.

ViewPager and PagerAdapter

A ViewPager is like a RecyclerView in some ways. A RecyclerView requires an Adapter to provide views. A ViewPager requires a PagerAdapter.

However, the conversation between ViewPager and PagerAdapter is much more involved than the conversation between RecyclerView and Adapter. Luckily, you can use FragmentStatePagerAdapter, a subclass of PagerAdapter, to take care of many of the details.

FragmentStatePagerAdapter will boil down the conversation to two simple methods: getCount() and getItem(int). When your getItem(int) method is called for a position in your array of crimes, it will return a CrimeFragment configured to display the crime at that position.

In CrimePagerActivity, set the ViewPager’s pager adapter and implement its getCount() and getItem(int) methods.

Listing 11.2  Setting up pager adapter (CrimePagerActivity.java)

public class CrimePagerActivity extends AppCompatActivity {

    private ViewPager mViewPager;
    private List<Crime> mCrimes;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_crime_pager);

        mViewPager = (ViewPager) findViewById(R.id.crime_view_pager);

        mCrimes = CrimeLab.get(this).getCrimes();
        FragmentManager fragmentManager = getSupportFragmentManager();
        mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {

            @Override
            public Fragment getItem(int position) {
                Crime crime = mCrimes.get(position);
                return CrimeFragment.newInstance(crime.getId());
            }

            @Override
            public int getCount() {
                return mCrimes.size();
            }
        });
    }
}

Let’s go through this code. After finding the ViewPager in the activity’s view, you get your data set from CrimeLab – the List of crimes. Next, you get the activity’s instance of FragmentManager.

Then you set the adapter to be an unnamed instance of FragmentStatePagerAdapter. Creating the FragmentStatePagerAdapter requires the FragmentManager. Remember that FragmentStatePagerAdapter is your agent managing the conversation with ViewPager. For your agent to do its job with the fragments that getItem(int) returns, it needs to be able to add them to your activity. That is why it needs your FragmentManager.

(What exactly is your agent doing? The short story is that it is adding the fragments you return to your activity and helping ViewPager identify the fragments’ views so that they can be placed correctly. More details are in the section called For the More Curious: How ViewPager Really Works.)

The pager adapter’s two methods are straightforward. The getCount() method returns the number of items in the array list. The getItem(int) method is where the magic happens. It fetches the Crime instance for the given position in the data set. It then uses that Crime’s ID to create and return a properly configured CrimeFragment.

Integrating CrimePagerActivity

Now you can begin the process of decommissioning CrimeActivity and putting CrimePagerActivity in its place.

First, add a newIntent method to CrimePagerActivity along with an extra for the crime ID.

Listing 11.3  Creating newIntent (CrimePagerActivity.java)

public class CrimePagerActivity extends AppCompatActivity {
    private static final String EXTRA_CRIME_ID =
            "com.bignerdranch.android.criminalintent.crime_id";

    private ViewPager mViewPager;
    private List<Crime> mCrimes;

    public static Intent newIntent(Context packageContext, UUID crimeId) {
        Intent intent = new Intent(packageContext, CrimePagerActivity.class);
        intent.putExtra(EXTRA_CRIME_ID, crimeId);
        return intent;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_crime_pager);

        UUID crimeId = (UUID) getIntent()
                .getSerializableExtra(EXTRA_CRIME_ID);
        ...
    }
}

Now, you want pressing a list item in CrimeListFragment to start an instance of CrimePagerActivity instead of CrimeActivity.

Return to CrimeListFragment.java and modify CrimeHolder.onClick(View) to start a CrimePagerActivity.

Listing 11.4  Firing it up (CrimeListFragment.java)

private class CrimeHolder extends RecyclerView.ViewHolder
        implements View.OnClickListener {
    ...
    @Override
    public void onClick(View view) {
        Intent intent = CrimeActivity.newIntent(getActivity(), mCrime.getId());
        Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
        startActivity(intent);
    }
}

You also need to add CrimePagerActivity to the manifest so that the OS can start it. While you are in the manifest, remove CrimeActivity’s declaration. To accomplish this, you can just rename the CrimeActivity to CrimePagerActivity in the manifest.

Listing 11.5  Adding CrimePagerActivity to the manifest (AndroidManifest.xml)

<manifest ...>
    ...
    <application ...>
    ...
        <activity
            android:name=".CrimeActivity"
            android:name=".CrimePagerActivity">
        </activity>

Finally, to keep your project tidy, delete CrimeActivity.java from the project tool window.

Run CriminalIntent. Press Crime #0 to view its details. Then swipe left and right to browse the crimes. Notice that the paging is smooth and there is no delay in loading. By default, ViewPager loads the item currently onscreen plus one neighboring page in each direction so that the response to a swipe is immediate. You can tweak how many neighboring pages are loaded by calling setOffscreenPageLimit(int).

Your ViewPager is not yet perfect. Press the Back button to return to the list of crimes and press a different item. You will see the first crime displayed again instead of the crime that you asked for.

By default, the ViewPager shows the first item in its PagerAdapter. You can have it show the crime that was selected by setting the ViewPager’s current item to the index of the selected crime.

At the end of CrimePagerActivity.onCreate(Bundle), find the index of the crime to display by looping through and checking each crime’s ID. When you find the Crime instance whose mId matches the crimeId in the intent extra, set the current item to the index of that Crime.

Listing 11.6  Setting the initial pager item (CrimePagerActivity.java)

public class CrimePagerActivity extends AppCompatActivity {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        FragmentManager fragmentManager = getSupportFragmentManager();
        mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
            ...
        });

        for (int i = 0; i < mCrimes.size(); i++) {
            if (mCrimes.get(i).getId().equals(crimeId)) {
                mViewPager.setCurrentItem(i);
                break;
            }
        }
    }
}

Run CriminalIntent again. Selecting any list item should display the details of the correct Crime. And that is it. Your ViewPager is now fully armed and operational.

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

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