Proper implementation of ViewPager2 in Android
UPDATE 7
Check : Migrate from ViewPager to ViewPager2
Check : Create swipe views with tabs using ViewPager2
UPDATE 6
Check out my answer if you want to implement Carousel using View Pager2
UPDATE 5
How to use TabLayout with ViewPager2
SAMPLE CODE
Use below dependencies
implementation 'com.google.android.material:material:1.1.0-alpha08'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
SAMPLE CODE
XMl layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
app:layout_anchor="@id/tabs"
app:layout_anchorGravity="bottom"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Activity
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import com.google.android.material.tabs.TabLayoutMediator
import com.google.android.material.tabs.TabLayout
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// setSupportActionBar(toolbar)
viewpager.adapter = AppViewPagerAdapter(supportFragmentManager, lifecycle)
TabLayoutMediator(tabs, viewpager, object : TabLayoutMediator.OnConfigureTabCallback {
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
// Styling each tab here
tab.text = "Tab $position"
}
}).attach()
}
}
OUTPUT
TabLayout with ViewPager2
From Docs
ViewPager2
New features
- Right-to-left (RTL) layout support
- Vertical orientation support
- notifyDataSetChanged fully functional
API changes
FragmentStateAdapter
replacesFragmentStatePagerAdapter
RecyclerView.Adapter
replacesPagerAdapter
registerOnPageChangeCallback
replacesaddPageChangeListener
SAMPLE CODE
add the latest
dependencies
forViewPager2
implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha01'
layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
activity
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import java.util.ArrayList;
public class MyActivity extends AppCompatActivity {
ViewPager2 myViewPager2;
MyAdapter MyAdapter;
private ArrayList<String> arrayList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
myViewPager2 = findViewById(R.id.view_pager);
arrayList.add("Item 1");
arrayList.add("Item 2");
arrayList.add("Item 3");
arrayList.add("Item 4");
arrayList.add("Item 5");
MyAdapter = new MyAdapter(this, arrayList);
myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
myViewPager2.setAdapter(MyAdapter);
}
}
MyAdapter
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private ArrayList<String> arrayList = new ArrayList<>();
public MyAdapter(Context context, ArrayList<String> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.tvName.setText(arrayList.get(position));
}
@Override
public int getItemCount() {
return arrayList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView tvName;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tvName);
}
}
}
New features
now we need to use
ViewPager2.OnPageChangeCallback()
to get Swipe event ofViewPager2
SAMPLE CODE
myViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
Log.e("Selected_Page", String.valueOf(position));
}
@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
we can set Orientation using
myViewPager2.setOrientation()
SAMPLE CODE
For HORIZONTAL Orientation
use
myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
For VERTICAL Orientation
use
myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
We can use
notifyDataSetChanged
same as we are using inRecyclerView.Adapter
SAMPLE CODE to add new item
btnAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
arrayList.add("New ITEM ADDED");
MyAdapter.notifyDataSetChanged();
}
});
SAMPLE CODE to remove new item
btnRemove.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
arrayList.remove(3);
MyAdapter.notifyItemRemoved(3);
}
});
UPDATE
Try this if you want to use Fragment
with ViewPager2
First create a
ViewPagerFragmentAdapter
class which extendsFragmentStateAdapter
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager2.adapter.FragmentStateAdapter;
public class ViewPagerFragmentAdapter extends FragmentStateAdapter {
private ArrayList<Fragment> arrayList = new ArrayList<>();
public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager) {
super(fragmentManager);
}
@NonNull
@Override
public Fragment getItem(int position) {
return arrayList.get(position);
}
public void addFragment(Fragment fragment) {
arrayList.add(fragment);
}
@Override
public int getItemCount() {
return arrayList.size();
}
}
Now use like this in your activity
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import neel.com.bottomappbar.R;
public class MainActivity extends AppCompatActivity {
ViewPager2 myViewPager2;
ViewPagerFragmentAdapter myAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myViewPager2 = findViewById(R.id.view_pager);
myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager());
// add Fragments in your ViewPagerFragmentAdapter class
myAdapter.addFragment(new FragmentOne());
myAdapter.addFragment(new Fragmenttwo());
myAdapter.addFragment(new FragmentThree());
// set Orientation in your ViewPager2
myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
myViewPager2.setAdapter(myAdapter);
}
}
for more information check this
- ViewPager2
- ViewPager2 under the Hood
- Hands on With ViewPager2
UPDATE 2
Version 1.0.0-alpha02
New features
- Ability to disable user input (
setUserInputEnabled
,isUserInputEnabled
)
API changes
ViewPager2
class final
Bug fixes
FragmentStateAdapter
stability fixes
SAMPLE CODE to disable swiping in viewpager2
myViewPager2.setUserInputEnabled(false);// SAMPLE CODE to disable swiping in viewpager2
myViewPager2.setUserInputEnabled(true);//SAMPLE CODE to enable swiping in viewpager2
UPDATE 3
Version 1.0.0-alpha03
New features
- Ability to programmatically scroll ViewPager2: fakeDragBy(offsetPx).
API changes
FragmentStateAdapter
now requires aLifecycle
object. Two utility constructors added to obtain it from the hostFragmentActivity
or the host Fragment
SAMPLE CODE
ViewPagerFragmentAdapter
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;
public class ViewPagerFragmentAdapter extends FragmentStateAdapter {
private ArrayList<Fragment> arrayList = new ArrayList<>();
public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment getItem(int position) {
return arrayList.get(position);
}
public void addFragment(Fragment fragment) {
arrayList.add(fragment);
}
@Override
public int getItemCount() {
return arrayList.size();
}
}
MainActivity code
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import neel.com.bottomappbar.R;
public class MainActivity extends AppCompatActivity {
ViewPager2 myViewPager2;
ViewPagerFragmentAdapter myAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myViewPager2=findViewById(R.id.view_pager);
myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager(), getLifecycle());
// add Fragments in your ViewPagerFragmentAdapter class
myAdapter.addFragment(new FragmentOne());
myAdapter.addFragment(new Fragmenttwo());
myAdapter.addFragment(new FragmentThree());
myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
myViewPager2.setAdapter(myAdapter);
}
}
UPDATE 4
Version 1.0.0-alpha05
New features
ItemDecorator
introduced with a behaviour consistent withRecyclerView
.MarginPageTransformer
introduced to provide an ability to create space between pages (outside of page inset).CompositePageTransformer
introduced to provide an ability to combine multiplePageTransformers
API changes
FragmentStateAdapter#getItem
method renamed toFragmentStateAdapter#createFragment
- previous method name has proven to be a source of bugs in the past.OFFSCREEN_PAGE_LIMIT_DEFAULT
value changed from 0 to -1. No need for a client code change if theOFFSCREEN_PAGE_LIMIT_DEFAULTconstant
used.
SAMPLE CODE
Activity code
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.MarginPageTransformer;
import androidx.viewpager2.widget.ViewPager2;
import neel.com.bottomappbar.R;
public class MainActivity extends AppCompatActivity {
ViewPager2 myViewPager2;
ViewPagerFragmentAdapter myAdapter;
private ArrayList<Fragment> arrayList = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myViewPager2 = findViewById(R.id.myViewPager2);
// add Fragments in your ViewPagerFragmentAdapter class
arrayList.add(new FragmentOne());
arrayList.add(new Fragmenttwo());
arrayList.add(new FragmentThree());
myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager(), getLifecycle());
// set Orientation in your ViewPager2
myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
myViewPager2.setAdapter(myAdapter);
myViewPager2.setPageTransformer(new MarginPageTransformer(1500));
}
}
ViewPagerFragmentAdapter
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;
public class ViewPagerFragmentAdapter extends FragmentStateAdapter {
private ArrayList<Fragment> arrayList = new ArrayList<>();
public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment createFragment(int position) {
switch (position) {
case 0:
return new FragmentOne();
case 1:
return new Fragmenttwo();
case 2:
return new FragmentThree();
}
return null;
}
@Override
public int getItemCount() {
return 3;
}
}
implementation of viewPager2
yes that link helped . I changed these lines of codes:
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
@Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
tabLayout.selectTab(tabLayout.getTabAt(position));
}
});
How to use TabLayout with ViewPager2 in Android
You have to use this TabLayoutMediator
that mimics tabLayout.setupWithViewPager()
and sets up the ViewPager2
with Tablayout
. Otherwise, you will have to write your own adapter that will combine both parties.
Its code will look like this in Kotlin
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = tabTitles[position]
}.attach()
I can't find PageChangeListener in ViewPager2.?
There is registerOnPageChangeCallback
method for that:
pager.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
}
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
}
override fun onPageScrollStateChanged(state: Int) {
super.onPageScrollStateChanged(state)
}
})
Why FragmentStateAdapter not work in ViewPager2?
FragmentStateAdapter
in ViewPager2
is a little bit different than in ViewPager
as follows:
Instead of implementing
getCount()
, implementgetItemCount()
Instead of implementing
getItem(int position)
, implementcreateFragment(int position)
Constructor signature is different by adding
Lifecycle
argument
which should be included in super as well
So replace your ViewPagerFragmentAdapter
with below
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import java.util.ArrayList;
public class ViewPagerFragmentAdapter extends FragmentStateAdapter {
private ArrayList<Fragment> arrayList = new ArrayList<>();
public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
public void addFragment(Fragment fragment) {
arrayList.add(fragment);
}
@Override
public int getItemCount() {
return arrayList.size();
}
@NonNull
@Override
public Fragment createFragment(int position) {
// return your fragment that corresponds to this 'position'
return arrayList.get(position);
}
}
Also make sure to import/extend the right adapter of ViewPager2
import androidx.viewpager2.adapter.FragmentStateAdapter;
UPDATE 2021
This answered the original issue of the question; However creating the FragmentStateAdapter
this way is not that right generally speaking; because of:
To have a high performant
ViewPager
, it should keep only a few instantiated off-screen page fragments.Keeping all the page fragments into a list is not good in terms of wasting device resources.
createFragment()
, as its name implies, is intended to create a new fragment for a particular page and return it; not to return a fragment from a list that has already instantiated fragments.Above all as per documentation createFragment()::
Provide a new Fragment associated with the specified position.
The adapter will be responsible for the Fragment lifecycle:
- The Fragment will be used to display an item.
- The Fragment will be destroyed when it gets too far from the
viewport, and its state will be saved. When the item is close to the
viewport again, a new Fragment will be requested, and a previously
saved state will be used to initialize it.
So, I'd suggest to change this design to include that into consideration:
Assuming your page fragment is PageFragment
; then you can have single instance pattern by having static PageFragment newInstance(int position) {}
method in the PageFragment
, and then the createFragment()
should be then:
@NonNull
@Override
public Fragment createFragment(int position) {
// return a brand new fragment that corresponds to this 'position'
return PageFragment.newInstance(position); // Create a new instance of the page fragment
}
Related Topics
Android Get Real Path by Uri.Getpath()
Communication Between Activity and Service
How to Make My App a Device Owner
Put Constant Text Inside Edittext Which Should Be Non-Editable - Android
How to Modify Arrayadapter in Listview: Unsupportedoperationexception
Passing Image from One Activity Another Activity
List Filter Custom Adapter Dont Give Result
How to Use Algolia Query in Firestorerecycleroptions
How to Have Placeholders in Strings.Xml for Runtime Values
How to Underline Text in an Android Layout
How to Add a Fragment to an Activity with a Programmatically Created Content View
How to Make a Countdown Timer in Android
Eclipse Giving Error, Missing R.Java File After Recent Update
Difference Between Getdefaultsharedpreferences and Getsharedpreferences
How to Send a File in Android from a Mobile Device to Server Using Http