tinkoff-mobile-tech / ScrollingPagerIndicator

Pager indicator inspired by Instagram. Lightweight and easy to set up.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Attach to ViewPager2

MarioNoll opened this issue · comments

Would be nice to use this with ViewPager2.

Hi there,

I ran into the same scenario where I was using ViewPager2 and wanted to use ScrollingPagerIndicator Library along with it.

So, I came up with the following solution.

Create a File with the following Code

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;

import ru.tinkoff.scrollingpagerindicator.ScrollingPagerIndicator;

public class ViewPager2Attacher implements ScrollingPagerIndicator.PagerAttacher<ViewPager2> {

    private RecyclerView.AdapterDataObserver dataSetObserver;
    private RecyclerView.Adapter attachedAdapter;
    private ViewPager2.OnPageChangeCallback onPageChangeListener;
    private ViewPager2 pager;

    @Override
    public void attachToPager(@NonNull final ScrollingPagerIndicator indicator, @NonNull final ViewPager2 pager) {
        attachedAdapter = pager.getAdapter();
        if (attachedAdapter == null) {
            throw new IllegalStateException("Set adapter before call attachToPager() method");
        }

        this.pager = pager;

        indicator.setDotCount(attachedAdapter.getItemCount());
        indicator.setCurrentPosition(pager.getCurrentItem());

        dataSetObserver = new RecyclerView.AdapterDataObserver() {
            @Override
            public void onChanged() {
                indicator.reattach();
            }
        };
        attachedAdapter.registerAdapterDataObserver(dataSetObserver);

        onPageChangeListener = new ViewPager2.OnPageChangeCallback() {

            boolean idleState = true;

            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixel) {
                final float offset;
                // ViewPager2 may emit negative positionOffset for very fast scrolling
                if (positionOffset < 0) {
                    offset = 0;
                } else if (positionOffset > 1) {
                    offset = 1;
                } else {
                    offset = positionOffset;
                }
                indicator.onPageScrolled(position, offset);
            }

            @Override
            public void onPageSelected(int position) {
                if (idleState) {
                    indicator.setDotCount(attachedAdapter.getItemCount());
                    indicator.setCurrentPosition(pager.getCurrentItem());
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                idleState = state == ViewPager2.SCROLL_STATE_IDLE;
            }
        };

        pager.registerOnPageChangeCallback(onPageChangeListener);
    }

    @Override
    public void detachFromPager() {
        attachedAdapter.unregisterAdapterDataObserver(dataSetObserver);
        pager.unregisterOnPageChangeCallback(onPageChangeListener);
    }
}

and then attach ViewPager2 with ScrollingPagerIndicator using the following line

{ScrollingPagerIndicator}.attachToPager({ViewPager2}, ViewPager2Attacher())

Voila!
That's it.

Enjoy!

@vishal1337 Looking at the current v.1.2.0 looks the same as your proposed solution. However there's an issue with it if you're using an adapter like ListAdapter that dispatches insert / delete / changed events and not a general change event via notifyDataSetChanged resulting in crashes / inconsistencies.
attachToRecyclerView has the correct implementation. A workaround is using the attachToRecyclerView and using ViewPager2.getChild(0) as RecyclerView as the target but this should be fixed in the library.

Works fine:

Before:
indicator.attachToPager(viewPager2)

After:
indicator.attachToRecyclerView(viewPager2.getChildAt(0) as RecyclerView)