Android working with ViewPager2, TabLayout and Page Transformers

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.

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.

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.

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



Source link