8

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

karanatwal.github.io
  • 2,880
  • 1
  • 19
  • 51
Anurag Singh
  • 5,580
  • 2
  • 24
  • 43
  • This isn't really a question. What is your question? If your question is "how do I do this?", what have you tried? You might consider researching how to use `NestedScrollView` with a regular `ViewPager`, and see if its techniques can be adapted to your scenario. Or, perhaps experiment with using `RecyclerView` instead of your `VerticalViewPager`, though this would require you to switch from fragments to views for your pages. – CommonsWare Apr 19 '18 at 04:34
  • Yes, how do I do this? My question is "How to make this work using the current implementation that I have mentioned in the question?". I tried using RecyclerView(item width and height set to match_parent), by wrapping it's each child in NestedScrollView but no luck. Having no luck I switched to VerticalViewPager where each page is again wrapped in NestedScrollView. How to make content scroll while making the page itself scroll or switch to next page either using RecyclerView or VerticalViewPager? – Anurag Singh Apr 19 '18 at 07:26
  • @CommonsWare It's just like how to make scrolling inside child or page work with child being wrapped in another ScrollView(RecyclerView or ViewPager) like group? Do let me know if I am missing any information. – Anurag Singh Apr 19 '18 at 08:18
  • 2
    I have never used `NestedScrollView`, so I do not know of a solution for getting it to work for your situation. I have posted a bounty to see if we can get an answer. – CommonsWare Apr 19 '18 at 20:18
  • @CommonsWare Sure. Thank-you for :-) – Anurag Singh Apr 20 '18 at 03:40
  • @AnuragSingh, can you post a simple project at github with the setup that you have? – azizbekian Apr 20 '18 at 07:46
  • @azizbekian Check, the answer for the sample project. – Anurag Singh Apr 20 '18 at 10:51
  • Have a look at this Q to see if it helps, https://stackoverflow.com/questions/49238797/delegate-touch-event-from-scrollview-to-vertical-viewpager/49362533#49362533 – darnmason Apr 24 '18 at 01:05

1 Answers1

6

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.

karanatwal.github.io
  • 2,880
  • 1
  • 19
  • 51
  • NestedScrollView.computeVerticalScrollRange(), NestedScrollView.computeVerticalScrollOffset(), NestedScrollView.computeVerticalScrollExtent(), can only be called from within the same library group (groupId=com.android.support) This API has been flagged with a restriction that has not been met. – Anurag Singh Apr 26 '18 at 13:59
  • Restricted methods shouldn't be accessed from the android framework as it is not acceptable and the restriction could be more tight in future or can be changed anytime. This will make application fail to compile and break the current functionality obviously. – Anurag Singh Apr 26 '18 at 15:10
  • Don't worry you can use `@SuppressLint("RestrictedApi")` . See this - https://stackoverflow.com/questions/41150995/appcompatactivity-oncreate-can-only-be-called-from-within-the-same-library-group – karanatwal.github.io Apr 27 '18 at 05:01
  • 2
    Anurag Singh is right, those methods are restricted for a reason, Google didn't want to make them part of the API so they can change or disappear whenever they want. – Leo supports Monica Cellio Apr 27 '18 at 12:00
  • I don't have much time right now, guys @LeonardoAcevedo ,Anurag rather than downvoting you should keep try the alternate way or you should have R&D on it. Downvoting and sit still is not the solution, Still i am posting the solution, you can check the edit. – karanatwal.github.io Apr 27 '18 at 12:11
  • 1
    @KarandeepAtwal, Thank you for taking the time out of your busy schedule and posting a solution. I still modifying the solution posted by you to overcome the RestrictedApi access and modify my current approach of nested scrolling. FYKI, I didn't down vote. I wasn't aware that there is a feature also of knowing who down voted. – Anurag Singh Apr 27 '18 at 13:49