As we all know, the Android team is constantly releasing new updates and improvements to the Android Framework. One of the components that received major updates is ViewPager2. ViewPager2 is the replacement of ViewPager and got few performance improvements and additional functionalities. In order to use ViewPager2, you should consider using androidx artifacts in your project.

In my earlier tutorial, I have explained Building Intro Sliders to your App using ViewPager. In this article, we are going to try the same but using ViewPager2. We’ll also go through the additional interesting functionalities provided by ViewPager2.

android-viewpager2-examples-tutorial

Whats’s New in ViewPager2

  • ViewPager2 is improvised version of ViewPager that provides additional functionalities and addresses common issues faced in ViewPager.
  • ViewPager2 is built on top of RecyclerView. So you will have great advantages that a RecyclerView has. For example, you can take advantage of DiffUtils to efficiently calculate the difference between data sets and update the ViewPager with animations.
  • ViewPager2 supports vertical orientation. Earlier vertical swiping is done by customizing the ViewPager.
  • ViewPager2 has right-to-left(RTL) support and this will be enabled automatically based on app locale.
  • When working with a collection of Fragments, calling notifyDatasetChanged() will update the UI properly when an underlying Fragment changes its UI.
  • ViewPager2 supports page transformations i.e you can provide animations when switching between pages. You can also write your own custom page transformation.
  • PagerAdapter is replaced by RecyclerView.Adapter as its based on RecyclerView.
  • FragmentStatePagerAdapter is replaced by FragmentStateAdapter.

To get started with ViewPager2, add the dependency to app/build.gradle.

implementation "androidx.viewpager2:viewpager2:1.0.0"

1. ViewPager with Static Views

Usually, ViewPager is used to achieve a tabbed view by combining the TabLayout and collection of Fragments. But if you want to have static views without the Fragment class, you can do it using RecyclerView adapter class.

Below is an example of implementing Intro Slides using ViewPager2. The designs are taken from my older example How to Build Intro Slider for your App but the implementation part varies due to changes in ViewPager2 and in adapter class.

Add the ViewPager2 to your layout file. You can see namespace is uses androidx package androidx.viewpager2.widget.ViewPager2.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto".../>

<androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

<RelativeLayout/>
public class ViewsSliderActivity extends AppCompatActivity {
    private ViewsSliderAdapter mAdapter;
    private TextView[] dots;
    private int[] layouts;
    private ActivityViewsSliderBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityViewsSliderBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        init();
    }

    private void init() {
        // layouts of all welcome sliders
        // add few more layouts if you want
        layouts = new int[]{
                R.layout.slide_one,
                R.layout.slide_two,
                R.layout.slide_three,
                R.layout.slide_four};

        mAdapter = new ViewsSliderAdapter();
        binding.viewPager.setAdapter(mAdapter);
        binding.viewPager.registerOnPageChangeCallback(pageChangeCallback);

        binding.btnSkip.setOnClickListener(v -> launchHomeScreen());

        binding.btnNext.setOnClickListener(v -> {
            // checking for last page
            // if last page home screen will be launched
            int current = getItem(+1);
            if (current < layouts.length) {
                // move to next screen
                binding.viewPager.setCurrentItem(current);
            } else {
                launchHomeScreen();
            }
        });

        binding.iconMore.setOnClickListener(view -> {
            showMenu(view);
        });

        // adding bottom dots
        addBottomDots(0);
    }

    /*
     * Adds bottom dots indicator
     * */
    private void addBottomDots(int currentPage) {
        dots = new TextView[layouts.length];

        int[] colorsActive = getResources().getIntArray(R.array.array_dot_active);
        int[] colorsInactive = getResources().getIntArray(R.array.array_dot_inactive);

        binding.layoutDots.removeAllViews();
        for (int i = 0; i < dots.length; i++) {
            dots[i] = new TextView(this);
            dots[i].setText(Html.fromHtml("&#8226;"));
            dots[i].setTextSize(35);
            dots[i].setTextColor(colorsInactive[currentPage]);
            binding.layoutDots.addView(dots[i]);
        }

        if (dots.length > 0)
            dots[currentPage].setTextColor(colorsActive[currentPage]);
    }

    private int getItem(int i) {
        return binding.viewPager.getCurrentItem() + i;
    }

    private void launchHomeScreen() {
        Toast.makeText(this, R.string.slides_ended, Toast.LENGTH_LONG).show();
        finish();
    }

    private void showMenu(View view) {
        PopupMenu popup = new PopupMenu(this, view);
        MenuInflater inflater = popup.getMenuInflater();
        inflater.inflate(R.menu.pager_transformers, popup.getMenu());
        popup.setOnMenuItemClickListener(item -> {
            if (item.getItemId() == R.id.action_orientation) {
                binding.viewPager.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
            } else {
                binding.viewPager.setPageTransformer(Utils.getTransformer(item.getItemId()));
                binding.viewPager.setCurrentItem(0);
                binding.viewPager.getAdapter().notifyDataSetChanged();
            }
            return false;
        });
        popup.show();
    }

    /*
     * ViewPager page change listener
     */
    ViewPager2.OnPageChangeCallback pageChangeCallback = new ViewPager2.OnPageChangeCallback() {
        @Override
        public void onPageSelected(int position) {
            super.onPageSelected(position);
            addBottomDots(position);

            // changing the next button text 'NEXT' / 'GOT IT'
            if (position == layouts.length - 1) {
                // last page. make button text to GOT IT
                binding.btnNext.setText(getString(R.string.start));
                binding.btnSkip.setVisibility(View.GONE);
            } else {
                // still pages are left
                binding.btnNext.setText(getString(R.string.next));
                binding.btnSkip.setVisibility(View.VISIBLE);
            }
        }
    };

    public class ViewsSliderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

        public ViewsSliderAdapter() {
        }

        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(viewType, parent, false);
            return new SliderViewHolder(view);
        }

        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

        }

        @Override
        public int getItemViewType(int position) {
            return layouts[position];
        }

        @Override
        public int getItemCount() {
            return layouts.length;
        }

        public class SliderViewHolder extends RecyclerView.ViewHolder {
            public TextView title, year, genre;

            public SliderViewHolder(View view) {
                super(view);
            }
        }
    }
}

This creates horizontally swipeable views that are created using static XML layouts. The full code of this example can be found here.

android-viewpager-static-views

2. ViewPager with Tabs & Fragments

If you want to create swipeable views with Tabs, you can combine ViewPager2, TabLayout and Fragments.

To use TabLayout, add Material Components to your package. This makes the material TabLayout available in your project.

implementation 'com.google.android.material:material:1.2.0-alpha01'

Add TabLayout and ViewPage2 to your layout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".fragments.FragmentViewPagerActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        style="@style/Widget.MaterialComponents.TabLayout.Colored"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

Create required test Fragment classes MoviesFragment, EventsFragment and TicketsFragment.

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.fragment.app.Fragment;

import info.androidhive.viewpager2.R;

public class MoviesFragment extends Fragment {

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState
    ) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_movies, container, false);
    }
}

Finally, create an adapter class that provides Fragments to ViewPager. Here we can see ViewPagerFragmentAdapter extends FragmentStateAdapter.

package info.androidhive.viewpager2.fragments;

import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;

import com.google.android.material.tabs.TabLayoutMediator;

import info.androidhive.viewpager2.databinding.ActivityFragmentViewPagerBinding;

public class FragmentViewPagerActivity extends AppCompatActivity {

    ActivityFragmentViewPagerBinding binding;

    // tab titles
    private String[] titles = new String[]{"Movies", "Events", "Tickets"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityFragmentViewPagerBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        init();
    }

    private void init() {
        // removing toolbar elevation
        getSupportActionBar().setElevation(0);

        binding.viewPager.setAdapter(new ViewPagerFragmentAdapter(this));

        // attaching tab mediator
        new TabLayoutMediator(binding.tabLayout, binding.viewPager,
                (tab, position) -> tab.setText(titles[position])).attach();
    }

    private class ViewPagerFragmentAdapter extends FragmentStateAdapter {

        public ViewPagerFragmentAdapter(@NonNull FragmentActivity fragmentActivity) {
            super(fragmentActivity);
        }

        @NonNull
        @Override
        public Fragment createFragment(int position) {
            switch (position) {
                case 0:
                    return new MoviesFragment();
                case 1:
                    return new EventsFragment();
                case 2:
                    return new TicketsFragment();
            }
            return new MoviesFragment();
        }

        @Override
        public int getItemCount() {
            return titles.length;
        }
    }
}

android-viewpager-tab-layout

3. ViewPager2 Orientation

In a few scenarios, you might want to provide vertical swiping instead of traditional horizontal swiping. To enable vertical swiping, add android:orientation to ViewPager2 element.

<androidx.viewpager2.widget.ViewPager2
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

Vertical orientation can also be enabled programmatically by calling setOrientation() method.

viewPager.setOrientation(ViewPager2.ORIENTATION_VERTICAL);

4. ViewPager Transformers

Another great feature of ViewPager2 is, page transformations i.e the page transition animation from one page to another. Traditionally, we see a sliding animation between two screens. This animation can be customized by providing page transformers to ViewPage2.

To set a custom transformation, use setPageTransformer() method. Below example creates Flip animation between pages.

package info.androidhive.viewpager2.transformers;

import android.view.View;

import androidx.viewpager2.widget.ViewPager2;

public class HorizontalFlipTransformation implements ViewPager2.PageTransformer {
    @Override
    public void transformPage(View page, float position) {

        page.setTranslationX(-position * page.getWidth());
        page.setCameraDistance(12000);

        if (position < 0.5 && position > -0.5) {
            page.setVisibility(View.VISIBLE);
        } else {
            page.setVisibility(View.INVISIBLE);
        }


        if (position < -1) {     // [-Infinity,-1)
            page.setAlpha(0);

        } else if (position <= 0) {    // [-1,0]
            page.setAlpha(1);
            page.setRotationY(180 * (1 - Math.abs(position) + 1));

        } else if (position <= 1) {    // (0,1]
            page.setAlpha(1);
            page.setRotationY(-180 * (1 - Math.abs(position) + 1));

        } else {
            page.setAlpha(0);
        }
    }
}

This creates beautiful vertical flip animation when swiping the pages.

viewpager page transformation horizontal flip

Below are a set of ViewPager transitions that I have collected from different sources (All the credits goes to original authors).

  1. Anti Clock Spin Transformation
  2. Clock Spin Transformation
  3. Cube In Depth Transformation
  4. Cube In Rotation Transformation
  5. Cube In Scaling Transformation
  6. Cube Out Depth Transformation
  7. Cube Out Rotation Transformation
  8. Cube Out Scaling Transformation
  9. Depth Page Transformer
  10. Depth Transformation
  11. Fade Out Transformation
  12. Fan Transformation
  13. Fidget Spin Transformation
  14. Gate Transformation
  15. Horizontal FlipTransformation
  16. Pop Transformation
  17. Spinner Transformation
  18. Toss Transformation
  19. Vertical Flip Transformation
  20. Vertical Shut Transformation
  21. Zoom Out Page Transformer

Transformations References:
Viewpager-Transformation
Loginworks

Subscribe
Notify of
guest
19 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Gianluca
Gianluca
10 months ago

Github link doesn’t work. Can you update?

Ravi Tamada
10 months ago
Reply to  Gianluca

I forgot to make the project public. Pls check now.

Thanks for notifying me:)

etinge mabian
etinge mabian
10 months ago

I can’t find the ActivityViewsSliderBinding class

Ravi Tamada
9 months ago
Reply to  etinge mabian

You have to enable view binding in your gradle. Refer the example project.

Fenny Manohar Reddy
Fenny Manohar Reddy
10 months ago

precise and great explanation !!!!!

Ravi Tamada
9 months ago

Thank You.

Thato Trer Rammoko
Thato Trer Rammoko
9 months ago

Hi Ravi, thank you for these tutorials they help me a lot, quick off topic question. Do you have a Graph QL + Android tutorial in the pipeline?

Ravi Tamada
9 months ago

No Thato, I haven’t planned any.

Igor Ganapolsky
8 months ago

You don’t like Kotlin?

somaji
somaji
8 months ago

import info.androidhive.viewpager2.databinding.ActivityFragmentViewPagerBinding;
in part2 AS telling ‘unkown’

Ravi Tamada
8 months ago
Reply to  somaji

Clean your project and build again. Also make sure, viewBinding is enabled in your gradle file.
https://github.com/ravi8x/ViewPager2-Examples/blob/master/app/build.gradle#L31

esQmo
esQmo
6 months ago

Is it possible reference the views inside those layout from an activity?
I added some views and I want to reference them and obviously i’m
getting NPE

esQmo
esQmo
6 months ago
Reply to  esQmo

:disqus please answer

Yuki Cheung
5 months ago

I follow this tutorial but the BottomDots don’t show on lunch the app first screen, but if I swipe right to second screen and back to first screen, or just pause and resume the app, the BottomDots can be seen on first screen. So wired. Can you give me some advices?

Rizki Adi Saputra
4 months ago

WHAT THE HELL IS “ActivityFragmentViewPagerBinding”

abed
abed
1 month ago

Thanks a lot for this amazing Tutorial !

I can’t find the class

import info.androidhive.viewpager2.databinding.ActivityFragmentViewPagerBinding;

where I can find it ?

abed
abed
1 month ago
Reply to  Ravi Tamada

I can’t find the class
could you please send it to me?

abed.benamar@gmail.com

19
0
Would love your thoughts, please comment.x
()
x