Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle scrolling for VerticalViewPager with NestedScrollView inside

Background:

I am using a variant of ViewPager which instead of scrolling in horizontal direction, it scrolss in vertical direction.

VerticalViewPager: https://android.googlesource.com/platform/packages/apps/DeskClock/+/master/src/com/android/deskclock/VerticalViewPager.java

android.support.v4.app.FragmentStatePagerAdapter is being used to set the contents in VerticalViewPager.

I am creating and returning a new instance of android.support.v4.app.Fragment from FragmentStatePagerAdapter every time getItem() of FragmentStatePagerAdapter is called as listed below:

@Override
public Fragment getItem(int position) {
   return  PreviewFragment.getNewFragmentInstance(position);
}

PreviewFragment renders the content that may not fit on the screen. In order to show all the contents that doesn't fit on to the screen, android.support.v4.widget.NestedScrollView is used as a parent element of PreviewFragment layout.

Problem:

Content of PreviewFragment does scroll for position 0 as expected. But swiping to page for position 1 is very difficult in terms that one has to perform fling or swipe operation many times in order to switch to next page.

Also, fling or swipe back to position 0 is very very difficult or almost impossible.

Required:

Fling or swipe or switch to next instance of PreviewFragment should be smooth. In other words scrolling of content should be smooth while keeping in mind that the swipe to next page should also work smoothly.

Link to the sample project as mentioned above

like image 725
Anurag Singh Avatar asked Apr 17 '18 12:04

Anurag Singh


1 Answers

i go through your Github code. I made it work for you. I have sent a pull request too. Problem was that onInterceptTouchEvent if returned true it makes ViewPager to scroll and if we return it false it disable ViewPager touch and make NestedScrollView to scroll. So here i did was to check if NestedScrollView needs to be scrolled then i returned it false if NestedScrollView is on top or bottom, I returned it to true. I am posting code here. You can modify it and make it more clean.

Github Link - https://github.com/karanatwal/vertical-viewpager-nestedscrollview

Below is VerticalViewPager code -

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    boolean toIntercept = super.onInterceptTouchEvent(flipXY(ev));
    // Return MotionEvent to normal


    float x = ev.getX();

    flipXY(ev);
    int direction=1;

    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mStartDragX = x;
            break;

        case MotionEvent.ACTION_MOVE:
            if (mStartDragX < x ) {
                direction=-1;
            } else if (mStartDragX > x) {
                direction=1;
            }
            MainActivity.MyPagerAdapter adapter = (MainActivity.MyPagerAdapter)this.getAdapter();
            if (adapter.getCurrentFragment() instanceof FragmentViewPager){
                FragmentViewPager fragmentViewPager = (FragmentViewPager)adapter.getCurrentFragment();
                toIntercept = !fragmentViewPager.getNestedScrollView().canScrollVertically(direction);
                int range = fragmentViewPager.getNestedScrollView().computeVerticalScrollRange();
                int offset = fragmentViewPager.getNestedScrollView().computeVerticalScrollOffset();
                int extent = fragmentViewPager.getNestedScrollView().computeVerticalScrollExtent();
                int percentage = (int)(100.0 * offset / (float)(range - extent));

                System.out.println("  range: "+range);
                System.out.print("  offset: "+offset);
                System.out.print("  extent: "+extent);                  
                System.out.print("  percentage: "+percentage);
                System.out.print("  direction: "+direction);

                if (fragmentViewPager.getNestedScrollView().computeVerticalScrollOffset()==0 && direction==-1){
                    //top
                    return true;
                }
                else if (percentage>99 && direction==1){
                    //bottom
                    return true;
                }else {
                    //scroll nestedscrollview
                    return false;
                }

            }

            break;
    }


    return toIntercept;
}

Your MyPagerAdapter -

public class MyPagerAdapter extends FragmentStatePagerAdapter {

    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    private Fragment mCurrentFragment;

    public Fragment getCurrentFragment() {
        return mCurrentFragment;
    }
    //...
    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        if (getCurrentFragment() != object) {
            mCurrentFragment = ((Fragment) object);
        }
        super.setPrimaryItem(container, position, object);
    }

    @Override
    public android.support.v4.app.Fragment getItem(int pos) {
        switch (pos) {
            case 0:
                return FragmentViewPager.newInstance("Title Start", R.drawable.ic_launcher_background);
            case 1:
                return FragmentViewPager.newInstance("Rock", R.drawable.rock);
            case 2:
                return FragmentViewPager.newInstance("Paper", R.drawable.paper);
            case 3:
                return FragmentViewPager.newInstance("Scissors", R.drawable.scissors);
            case 4:
                return FragmentViewPager.newInstance("Title End", R.drawable.ic_launcher_background);
            default:
                return FragmentViewPager.newInstance("Default Title", R.drawable.rock);
        }
    }

    @Override
    public int getCount() {
        return 5;
    }
}

For avoiding Restricted API methods- Use below logic as a alternate

 //offset , range and extent
            int scrollY = scrollView.getScrollY();               
            int scrollContentHeight = scrollView.getChildAt(0).getHeight();
            int screenHeight = Utility.getScreenHeight(context); 
            int statusBarHeight = Utility.getStatusBarHeight(context);

            double percent = ((((float) scrollY) / ((float) (scrollContentHeight - screenHeight + statusBarHeight))));

All you need is percentage of scroll and direction of swipe. There are alot more questions on SO for finding scroll percentage and swipe direction like this and this. You should do little R&D for it.

like image 70
karanatwal.github.io Avatar answered Oct 19 '22 11:10

karanatwal.github.io