Reputation: 139
How can we implement infinite scrolling in ViewPager2
As there is no OnPageChangeListener()
in this pager.
But there is setPageTransformer()
Listener which get callback whenever we change page.
So any solution for viewpager2 problem
Note : I'm using recyclerview adapter for viewpager2
Upvotes: 4
Views: 9579
Reputation: 1616
Since you mentioned you were using recyclerview adapter for viewpager2 have you tried writing it as you would while implementing infinite scroll in a recyclerview.
Basically you need to return Integer.MAX_VALUE
in your adapter's getItemCount()
method and whenever in onBindViewHolder
you need actual item position always use position % yourList.size()
. I've also added the size > 0
check in order to avoid crashes when list is empty and therefore this would otherwise throw a division by zero error.
Below is a sample adapter, you can use it as reference for your adapter and check if it works well for you. Hope this helps.
public class SampleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private List<Sample> mSampleList;
public SampleAdapter(List<Sample> mSampleList) {
this.mSampleList = mSampleList;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View viewCommonItems = LayoutInflater.from(parent.getContext())
.inflate(R.layout.demo_view, parent, false);
ItemViewHolder commonHolders = new ItemViewHolder(viewCommonItems);
return commonHolders;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ItemViewHolder viewHolder = (ItemViewHolder) holder;
if(mSampleList.size()>0) {
Sample sample = mSampleList.get(position % mSampleList.size());
Glide.with(viewHolder.itemView.getContext()).load(sample.getImage())
.into(viewHolder.ivImage);
}
}
@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
public class ItemViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.iv_img)
ImageView ivImage;
public ItemViewHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
Upvotes: 3
Reputation: 3602
For infinite scrolling, you don't have to customise the view pager. The trick in the viewPager adapter. You can return Int.MAX value in getCount override method. The in the instantiateItem override function you can use item_position % item_size to get the actual item position. Code example is given below.
class AdsSliderAdapter(
private val bannerImageList: MutableList<SliderImagesItem>,
private val callback: SliderItemClickListener) :PagerAdapter() {
private var mContext: Context? = null
override fun instantiateItem(container: ViewGroup, position: Int): Any {
mContext = container.context
val view = LayoutInflater.from(container.context)
.inflate(R.layout.item_ad_slider, container, false)
val adImage: AppCompatImageView = view.ivAd
mContext?.let {
GlideApp.with(it).load(bannerImageList[position % bannerImageList.size].imageUrl)
.into(adImage)
}
val viewPager = container as ViewPager
viewPager.addView(view, 0)
view.cardView.onClick { callback.onSliderImageClick(bannerImageList[position % bannerImageList.size]) }
return view
}
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return view === `object`
}
override fun getCount(): Int {
return if (bannerImageList.size > 0) {
Int.MAX_VALUE
} else {
0
}
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
val viewPager = container as ViewPager
val view = `object` as View
viewPager.removeView(view)
}
interface SliderItemClickListener {
fun onSliderImageClick(item: SliderImagesItem)
}
}
Upvotes: 9
Reputation: 1496
Try below code
@Override
public void onPageScrollStateChanged (int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
int curr = viewPager.getCurrentItem();
int lastReal = viewPager.getAdapter().getCount() - 2;
if (curr == 0) {
viewPager.setCurrentItem(lastReal, false);
} else if (curr > lastReal) {
viewPager.setCurrentItem(1, false);
}
}
}
I hope this can help You!
Thank You.
Upvotes: 0
Reputation: 1049
Step 1 : Create EndlessScrollAdapter class
class EndlessScrollAdapter internal constructor(
fm: FragmentManager,
lifeCycle: Lifecycle
) : FragmentStateAdapter(fm, lifeCycle) {
private val items = mutableListOf<Model>()
val firstElementPosition = Int.MAX_VALUE / 2
fun updateList(list: List<Model>) {
items.apply {
clear()
addAll(list)
}
notifyDataSetChanged()
}
override fun getItemCount(): Int = if (items.isNotEmpty()) Int.MAX_VALUE else 0
override fun createFragment(position: Int): Fragment = ViewPagerFragment(
items[position.rem(items.size)])
}
Step : 2 Call from Activity or Fragment
viewPager2.adapter = endlessScrollAdapter
endlessScrollAdapter.apply {
updateList(someModelList)
viewPager2.setCurrentItem(this.firstElementPosition, false)
}
Literally It's not endless but from the user perspective it is, as he will never reach to the edge. The length of ViewPager2
is Int.MAX_VALUE
, the start position is Int.MAX_VALUE/2
so user can scroll forward and backwards.
Upvotes: 5
Reputation: 812
You can use the code below
public class YourPagerAdapter extends FragmentStatePagerAdapter
{
public static int COUNT_OF_LOOP = 1000;
private ArrayList<Product> mProducts;
public YourPagerAdapter(FragmentManager manager, ArrayList<Product> products)
{
super(manager);
mProducts = products;
}
@Override
public Fragment getItem(int position)
{
if (mProducts != null && mProducts.size() > 0)
{
position = position % mProducts.size();
return MyFragment.newInstance(mProducts.get(position));
}
else
{
return MyFragment.newInstance(null);
}
}
@Override
public int getCount()
{
if (mProducts != null && mProducts.size() > 0)
{
return mProducts.size()*COUNT_OF_LOOP;
}
else
{
return 1;
}
}
}
mAdapter = new YourPagerAdapter(getSupportFragmentManager(), mProducts);
mViewPager.setAdapter(mAdapter);
mViewPager.setCurrentItem(mViewPager.getChildCount() * YourPagerAdapter. COUNT_OF_LOOP / 2, false);
Upvotes: 0