安卓:在垂直ViewPager中使用RecyclerView

13

我想在一个垂直的ViewPager中实现一个RecyclerView。我的当前布局如下所示。

VerticalViewpager
- Fragment1
    - ImageView
- Fragment2 
    - RecyclerView
如果我从 Fragment1 滑动到 Fragment2,一切都正常。我能够在 Recyclerview 内上下滚动。如果我尝试向后滑动/滚动回 Fragment1,则会出现问题。
如果用户已经滚动到了 RecyclerView 的顶部,那么我想将下一个“向上”滚动事件转发给垂直 ViewPager。我尝试重写 onInterceptTouchEvent 方法,但是不幸的是仍然没有成功。有什么想法吗?谢谢您的帮助 :)

1
嗨!我需要做完全相同的事情,有运气吗? - Caique
嗨@Caique,很遗憾没有适用于我们所有情况的解决方案。印度先生的建议对我们有所帮助,但正如我在他的建议下面的评论中已经提到的,这个解决方案并不总是有效。 - jennymo
谢谢你的回答。我的做法是:我重写了recyclerview的onTouch方法,从ACTION_DOWN、ACTION_MOVE和ACTION_UP中获取速度,如果速度大于500(带有一定的速度向上滑动),我就在viewpager上返回一个位置。效果还不错。我会把代码作为答案发布出来,也许能帮到你。 - Caique
那将非常好。一旦您发布它,我会尝试您的解决方案 :) - jennymo
8个回答

3

您需要禁用 scroll。尝试使用 recyclerView.setNestedScrollingEnabled(false);


1
嗨,谢谢你的回答。我试过了,有时候它可以工作... :D 如果我到达RecyclerView的顶部,我必须至少滚动四五次,直到VerticalViewpager获取事件并滚动回Fragment1。你有什么想法是什么原因导致这种行为? - jennymo

2

1) 您需要使用支持库23.2.0或更高版本 2) recyclerView的高度将会是wrap_content。 3) recyclerView.setNestedScrollingEnabled(false)

但是这样做会使recycler模式无法工作。(即所有视图一次性加载,因为wrap_content需要完整的recyclerView高度,所以它会一次性绘制所有的recycler视图。没有视图被回收)。请尽量避免使用此模式,除非真正需要。


1
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);
            }
            });

使用此代码,您可以查找用户是否处于顶部位置。在该位置,您可以使视图分页器移动到所需位置。

如果用户已经在回收视图中的顶部位置,并尝试向上滚动以触发视图页面,则检测将无法工作。 - Alexander Hoffmann
@AlexanderHoffmann:尝试将 int topRowVerticalPosition = 0; 声明为公共的。 - Jeevanandhan
我无法在onScroll方法中将变量设置为public。这样做也没有任何意义,不是吗? - Alexander Hoffmann
@AlexanderHoffmann:你也可以使用 recyclerView.getChildCount() == 0 来查找子项数量。 - Jeevanandhan

1
我建议首先迁移到ViewPager2,因为它本地支持垂直滚动,然后使用官方文档中提供的解决方案来支持嵌套可滚动元素。
基本上,您需要将NestedScrollableHost添加到项目中,然后将RecyclerView包装在其中,类似如下:
    <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>

请注意,此解决方案仅适用于您提供的布局,其中RecyclerView将是ViewPager屏幕的直接子项。此解决方案不适用于嵌套在主RecyclerView中的其他RecyclerView。

1
如果您可以从RecyclerView访问VerticalViewPager,则可以扩展RecyclerView并检查canScrollVertically(-1),然后将触摸事件转发到viewpager。

0
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);
                }
            }
        });

0

我曾经遇到过同样的问题。Mr.India的回答很有帮助,但正如你所评论的那样,它只在某些情况下有效。

我的做法是

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;
            }
        });

在 ACTION_UP 事件中,我会验证速度是否大于 500,这意味着它正在以一定的速度向上滚动,而 topRowVerticalPosition 表示它位于 RecyclerView 的顶部。然后,我获取 ViewPager 并将 setCurrentItem 设置为前一个项目。
这有些不太好的 hack,但这是我发现让纵向 ViewPager 在页面之间看起来像是良好滚动的唯一方法。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接