Android working with ViewPager2, TabLayout and Page Transformers
source link: https://www.androidhive.info/2020/01/viewpager2-pager-transformations-intro-slider-pager-animations-pager-transformations/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Android working with ViewPager2, TabLayout and Page Transformers
By Ravi Tamada January 21, 2020 19 CommentsAs 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.
<
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(
"•"
));
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.
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"
?>
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;
}
}
}
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.
Below are a set of ViewPager transitions that I have collected from different sources (All the credits goes to original authors).
Transformations References:
Viewpager-Transformation
Loginworks
Hi there! I am Founder at androidhive and programming enthusiast. My skills includes Android, iOS, PHP, Ruby on Rails and lot more. If you have any idea that you would want me to develop? Let’s talk: [email protected]
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK