How to create slider with ViewPager in Android

How to create slider with ViewPager in Android

One common UI requirement is sliding between multiple screens. For example, a photo slideshow. Android provides a UI control for creating sliding screens called the ViewPager. This article will explain using ViewPager to create a sliding screen UI in your Android apps.

Create a new Blank Activity project and begin by adding a ViewPager to the activity. Change activity_main.xml to the following:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_light"
    android:padding="5dp"
    android:orientation="vertical">

    <android.support.v4.view.ViewPager
        android:id="@+id/vpPager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.85">
    </android.support.v4.view.ViewPager>

    <me.relex.circleindicator.CircleIndicator
        android:id="@+id/indicator"
        android:layout_width="match_parent"
        android:layout_height="48dp" />
</LinearLayout>

In the above layout, there is a LinearLayout containing a ViewPager with an id of vpPage. There is also a view called CircleIndicator which indicates the dots at the bottom of the screen depending on the number of pages in the ViewPager.

As indicator you can use any of following

Next step is define Adapter wich will supply items for our ViewPager. There are two ways FragmentPagerAdapter and PagerAdapter.

Let's start with FragmentPagerAdapter. You need to define each screen in the ViewPager as a fragment. Each screen could be an instance of different Fragment classes, or different instances of the same fragment class with varying content. This app will contain three screens with ImageView and TextView.

The page_item.xml layout is as follow:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <ImageView
        android:id="@+id/ivIcon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        />

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:layout_gravity="center_horizontal"
        android:textSize="20dp"/>
</LinearLayout>

Result

android_slider_result.png

In following code PageFragment, has a getInstance method which takes the title and the image resource id displayed in the fragment. The fragment inflates the page_item.xml layout and sets the title and the image resource id.

public class PageFragment extends Fragment {
    private String title;
    private int image;

    public static PageFragment newInstance(String title, int resImage) {
        PageFragment fragment = new PageFragment();
        Bundle args = new Bundle();
        args.putInt("image", resImage);
        args.putString("title", title);
        fragment.setArguments(args);
        return fragment;
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        image = getArguments().getInt("image", 0);
        title = getArguments().getString("title");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_one_img, container, false);
        TextView tvTitle = (TextView) view.findViewById(R.id.tvTitle);
        tvTitle.setText(title);

        ImageView ivIcon = (ImageView) view.findViewById(R.id.ivIcon);
        ivIcon.setImageResource(image);
        return view;
    }
}

Now you’ve created the fragments you need a PagerAdapter which you will set on the ViewPager. Although you can directly extend from PagerAdapter, Android provides another base class FragmentPagerAdapter which implements basic functionality to show the fragments as pages in the PagerAdapter.

Extend the adapter from FragmentPagerAdapter by adding this class to MainActivity.java.

public static class PageAdapter extends FragmentPagerAdapter {
    private static int NUM_ITEMS = 3;

    public PageAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @Override
    public int getCount() {
        return NUM_ITEMS;
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return InfoFragment.newInstance("EMail", android.R.drawable.ic_dialog_email);
            case 1:
                return InfoFragment.newInstance("Alert", android.R.drawable.ic_dialog_alert);
            case 2:
                return InfoFragment.newInstance("Dialer", android.R.drawable.ic_dialog_dialer);
            default:
                return null;
        }
    }
}

The PageAdapter class extends from FragmentPagerAdapter and overrides the getCount method which returns how many pages the adapter will display. In this example, you want to show three pages, and it returns three. The getItem method returns the instance of the fragment to display on the screen.

Instead of FragmentPagerAdapter we can use basic PagerAdapter.

class CustomPageAdapter extends PagerAdapter {
    Context context;
    LayoutInflater layoutInflater;

    int[] resources = {R.drawable.image1, R.drawable.image2, R.drawable.image3};

    public CustomPagerAdapter(Context context) {
        context = context;
        layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
        return resources.length;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == ((LinearLayout) object);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View itemView = mLayoutInflater.inflate(R.layout.page_item, container, false);

        ImageView ivIcon = (ImageView) itemView.findViewById(R.id.ivIcon);
        ivIcon.setImageResource(resources[position]);

        container.addView(itemView);

        return itemView;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((LinearLayout) object);
    }
}

Let's discuss the four methods that we've overridden (mandatory):

  • Method getCount() should return the number of views available, i.e., number of pages to be displayed/created in the ViewPager.
  • Method instantiateItem() should create the page for the given position passed to it as an argument. In our case, we inflate() our layout resource to create the hierarchy of view objects and then set resource for the ImageView in it. Finally, the inflated view is added to the container (which should be the ViewPager) and return it as well.
  • Method destroyItem() removes the page from the container for the given position. We simply removed object using removeView() but could’ve also used removeViewAt() by passing it the position.
  • Method isViewFromObject() checks whether the View passed to it (representing the page) is associated with that key or not. The object returned by instantiateItem() is a key/identifier. It is required by a PagerAdapter to function properly. For our example, the implementation of this method is really simple, we just compare the two instances and return the evaluated boolean.

It is important to understand that as the user navigates to a particular page, the one next to it is generated by calling instantiateItem() while the one before the previous one gets destroyed by calling destroyItem(). This caching limit (destruction and rebuilding limit) can be specified by the setOffscreenPageLimit() method on the ViewPager object which is set to 1 by default. Increasing this value to a higher number leads to a smoother navigation as far as the animations and interactions are concerned as everything is retained in memory but then it can also cause a memory overhead affecting the app’s performance. So you’ve to find the perfect balance in your case.

Once you have created the PageAdapter you need to set the instance of it on the ViewPager in the onCreate function as below:

public class MainActivity extends AppCompatActivity {

    PagerAdapter adapter;

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

        ViewPager pager = (ViewPager) findViewById(R.id.vpPager);
        adapter = new PageAdapter(getSupportFragmentManager());
        vpPager.setAdapter(adapter);

        CircleIndicator indicator = (CircleIndicator) findViewById(R.id.indicator);
        indicator.setViewPager(pager);
    }
}

You can implement different animations on the page slide by setting a ViewPager.PageTransformer. To implement this you have to override the transformPage method. In this function you provide custom animations based on the position on the screen, there are a lot of transforms available as open source libraries which you can use, for example the ToxicBakery.ViewPagerTransforms.

To use a custom animation, add it to your dependencies in build.gradle (Module: App) as follows:

dependencies {
    ...
    compile 'com.ToxicBakery.viewpager.transforms:view-pager-transforms:1.2.32@aar'
}

Now you can add the PageTransformer on the ViewPager in the onCreate method as shown:

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

    ViewPager pager = (ViewPager) findViewById(R.id.vpPager);
    adapter = new PageAdapter(getSupportFragmentManager());
    vpPager.setAdapter(adapter);

    CircleIndicator indicator = (CircleIndicator) findViewById(R.id.indicator);
    indicator.setViewPager(pager);

    pager.setPageTransformer(true, new RotateUpTransformer());
}
comments powered by Disqus