The Android ViewPager is a view which allows users to swipe left and right through pages of data. It uses an instance of PagerAdapter
to generate pages which the ViewPager
displays. Several tutorials demonstrate using the ViewPager in conjunction with a Fragment, however if you are not working directly with an Activity this can be difficult. My experience implementing a ViewPager using Android’s documentation and search results was mildly frustrating.
The following is a demonstration of how to implement Android’s ViewPager
using the PagerAdapter
:
Include a ViewPager in your desired layout:
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/my_view_pager" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v4.view.ViewPager> |
Failing to use the support ViewPager can cause your application to have a RuntimeException
and crash:
java.lang.RuntimeException: Unable to start activity android.view.InflateException: Binary XML file line #28: Error inflating class ViewPager Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.ViewPager" on path: DexPathList |
Implementing the ViewPager:
For this demonstration I’m using a custom view to hold the ViewPager. This custom view includes an inner subclass of PagerAdapter which will manage the ViewPager views. The class is described in the code / comments below. The comments describe the purpose / usage of the required override methods of PagerAdapter instantiateItem()
, getCount()
, isViewFromObject()
and destroyItem()
. Also included is getItemPosition()
which is helpful when modifying, adding and deleting items from the dataset / ViewPager.
import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; // For this demonstration I'm using using a custom frame layout to hold the view pager. public class MyFrameLayout extends FrameLayout { // Views private MyViewPagerAdapter myViewPagerAdapter; // View Pager Adapter private ViewPager myViewPager; // View Pager // Layout inflater for creating views private LayoutInflater inflater; // FrameLayout Constructors public MyFrameLayout(Context context) { super(context); } public MyFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); } public MyFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } // Sample initializtion method to setup the views when ready public void initialize() { // Grab a reference to the ViewPager myViewPager = (ViewPager) findViewById(R.id.my_view_pager); // Construct a new PagerAdapter to manage the pages of the ViewPager myViewPagerAdapter = new MyViewPagerAdapter(); // Inner class - see below // Set the ViewPagers adapter myViewPager.setAdapter(myViewPagerAdapter); // Construct the layout inflater this.inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); } // Inner subclass of PagerAdapter to manage the ViewPager views class MyViewPagerAdapter extends PagerAdapter { // Constructor public MyViewPagerAdapter() {} // Required Override for the ViewPager to function properly. // The value returned here is used referenced by the ViewPager. If getCount() returns 0, no views will be created. // This is best used with a data set. Set this to return the size of your data set. // Using a default value of 5 to display 5 pages for this demonstration. @Override public int getCount() { return 5; } // Required Override for the ViewPager to function properly. // Determines whether a page View is associated with a specific key object as returned by instantiateItem(ViewGroup, int). @Override public boolean isViewFromObject(View view, Object o) { return view == o; } // Required Override for the ViewPager to function properly. // Removes a page for the given position. Called automatically by ViewPager as needed. @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } // Required Override for the ViewPager to function properly. // Creates pages for the given position automatically. // This function will fire automatically as required. On instantiation this will fire twice, once for // page one, once for page two. As users scroll it will fire to ensure views exist ahead and behind the active // item. This function was confusing to me initially. @Override public Object instantiateItem(ViewGroup container, int position) { // Inflate (and modify) the desired layout to add to the ViewPager. FrameLayout item = (FrameLayout) inflater.inflate(R.layout.view_to_add_to, container, false); // Add the inflated item to the ViewPager container.addView(item); // Ensure the item created and added to the container is returned return item; } // Used internally by the adapter when calling notifyDataSetChanged(). This method is referenced during this // call. Returning POSITION_NONE cases the previously created views to be destroyed and new ones created. // This was a big gotcha for me when modifying the data set to remove or add views from the ViewPager. @Override public int getItemPosition(Object object) { return POSITION_NONE; } } } |
Using Datasets / Modifying, adding and removing pages / views from the ViewPager:
Once you have an instance of PagerAdapter hooked to your ViewPager there is no need to call the remove()
or add()
methods of the view pager, the PagerAdapter handles it all. Keep the following in mind when using a dataset (whichever works best for you) with the PagerAdapter:
getCount()
should return the size of your dataset.
instantiateItem()
provides a position argument which can be used to get data from your dataset.
notifyDataSetChanged()
Call this after changing the associated dataset. This is the only call required to update the ViewPager pages / views, it will use the PagerAdapter instantiateItem() and destroyItem() calls automagically.
getItemPosition()
A bit of a trick when modifying the dataset / views in the ViewPager. Returning POSITION_NONE
will remove all views from the ViewPager when a dataset change is made (and notifyDataSetChanged()
is called). Since I did not use setOffscreenPageLimit()
the ViewPager should only be dealing with a maximum of 3 views so I consider this OK.
I hope this helps other potentially confused developers when implementing this view!