jennymo
jennymo

Reputation: 1480

Android: RecyclerView within vertical viewpager

I want to implement a recyclerview within a vertical viewpager. My current layout looks like the following

VerticalViewpager
- Fragment1
    - ImageView
- Fragment2 
    - RecyclerView

If I swipe from Fragment1 to Fragment2 everything works fine. I am able to scroll within the recyclerview up and down. The problem occurs if I try to swipe/scroll back to Fragment1.

If the user has scrolled to the top of the recyclerview, I would like to forward the next "Up"-Scroll event to the vertical viewpager. I have tried overriding the onInterceptTouchEvent method, but unfortunately still no success. Any ideas out there? Thanks for your help :)

Upvotes: 13

Views: 4498

Answers (8)

Calvin Rai
Calvin Rai

Reputation: 876

I would suggest migrating to ViewPager2 first, since it natively allows for vertical scrolling, and then using the solution provided by the official documentation to supported nested scrollable elements.

Essentially, you need to add the NestedScrollableHost to your project and then wrap your RecyclerView with it, similar to below:

    <NestedScrollableHost
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/my_recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" />

    </NestedScrollableHost>

Note that this solution only works for the layout your provided where the RecyclerView would be an immediate child of one of your ViewPager's screen. This solution won't work for other RecyclerView that are nested in the main RecyclerView.

Upvotes: 1

Rami
Rami

Reputation: 158

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled (RecyclerView recyclerView,int dx, int dy) {

            }
            @Override
            public void onScrollStateChanged (RecyclerView recyclerView,int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                if (!mRecyclerView.canScrollVertically(-1) && newState == 0) {

                    Log.e("canScrollVertically", mRecyclerView.canScrollVertically(-1)+"");

                    verticalViewPager.setCurrentItem(0, true);
                }
            }
        });

Upvotes: 0

Caique
Caique

Reputation: 86

I had the same problem. The answer from Mr.India was helpful, but as your commented, it only worked sometimes.

What I did was

recyclerView.setOnTouchListener(new View.OnTouchListener() {
            public float speed;
            public VelocityTracker mVelocityTracker;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int index = event.getActionIndex();
                int action = event.getActionMasked();
                int pointerId = event.getPointerId(index);

                switch (action) {

                    case MotionEvent.ACTION_DOWN:
                        if(mVelocityTracker == null) {
                            mVelocityTracker = VelocityTracker.obtain();
                        }
                        else {
                            mVelocityTracker.clear();
                        }
                        mVelocityTracker.addMovement(event);
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if(mVelocityTracker != null) {
                            mVelocityTracker.addMovement(event);
                            mVelocityTracker.computeCurrentVelocity(1000);
                            speed = VelocityTrackerCompat.getYVelocity(mVelocityTracker, pointerId);
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop();
                        if(speed > 500 && topRowVerticalPosition >= 0) {
                            ReaderFragmentParent parent = (ReaderFragmentParent) getActivity();
                            ReaderPager pager = parent.getPager();
                            if(pager != null) {
                                pager.setCurrentItem(pager.getCurrentItem()-1);
                            }

                        }
                        return false;
                    case MotionEvent.ACTION_CANCEL:
                        mVelocityTracker.recycle();
                        break;
                }


                return false;
            }
        });

On the ACTION_UP I verify if the speed > 500, it means it's going up on a considered speed and the topRowVerticalPosition means it is on top of the recyclerview. Then I get the ViewPager and setCurrentItem to previous item.

it's a bit of a dirty hack, but it was the only way I found to make look like a good scrolling between pages on the Vertical ViewPager

Upvotes: 0

Evan liu
Evan liu

Reputation: 17

You need to forbid nestedscroll in parent scrollview i have new implement for this , you can try the below link https://github.com/liuxiaocong/VerticalViewpage

Upvotes: 0

Ashok Varma
Ashok Varma

Reputation: 3539

1) You need to use support library 23.2.0 (or) above 2) and recyclerView height will be wrap_content. 3) recyclerView.setNestedScrollingEnabled(false)

But by doing this the recycler pattern don't work. (i.e all the views will be loaded at once because wrap_content needs the height of complete recyclerView so it will draw all recycler views at once. No view will be recycled). Try not to use this pattern util unless it is really required.

Upvotes: 2

Hyperion
Hyperion

Reputation: 382

If you have access VerticalViewPager from RecyclerView, you can extend RecyclerView and check canScrollVertically(-1) and forward touch event to viewpager

Upvotes: 1

Jeevanandhan
Jeevanandhan

Reputation: 1073

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrolled (RecyclerView recyclerView,int dx, int dy) {
                int topRowVerticalPosition = (recyclerView == null || recyclerView.getChildCount() == 0) ? 0 : recyclerView.getChildAt(0).getTop();
                if (topRowVerticalPosition >= 0) {
                    //top position
                }
            }
                @Override
                public void onScrollStateChanged (RecyclerView recyclerView,int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }
            });

By using this code you can find whether the user is in top position. In the position you can make the view pager to move to your desired position.

Upvotes: 1

Mr.India
Mr.India

Reputation: 674

You need do disable the scroll. Try using recyclerView.setNestedScrollingEnabled(false);

Upvotes: 3

Related Questions