ViewPager中的横向RecyclerView无法滚动。

10

我有一个位于复杂层次结构内部的水平RecyclerView,它看起来像这样 -

<ViewPager id="+@id/first">
    <ViewPager id="+@id/second"> this viewpager is taking away the scroll event
        <SwipeToRefreshLayout>
            <RecyclerView> //this one is vertical
                <RecyclerView id="@id/rv1"> //this one is horizontal
                <RecyclerView id="@id/rv2"> //this one is working fine (different type)
            </RecyclerView>
        </SwipeToRefreshLayout>
    </ViewPager>
</ViewPager>

现在的问题是第二个ViewPager正在劫持水平RV的滚动。该垂直RV中有两种类型的水平RV(RV1和RV2)。但只有其中一个(RV1)遇到了这个问题。第二个(RV2)可以正常工作。 另外,当我按住并保持不动时,滚动也可以正常工作。当RV1已经滚动且未解决时,滚动也可以正常工作。 我参考了其他答案,提到设置nestedScrolling为false。但似乎没有起作用。
5个回答

17

你可以通过覆盖onInterceptTouchEvent方法来实现:

 mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent event) {
                int action = event.getAction();
               
                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                        rv.getParent().requestDisallowInterceptTouchEvent(true);

                        break;
                
                }
                return false;
            }

            @Override
            public void onTouchEvent(@NonNull RecyclerView view, @NonNull MotionEvent event) {

            }

            @Override
            public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

            }
        });

2
但是它与 Recycler-View 交互时也会阻止屏幕的垂直移动。 - shehzy

7

这篇回答是@Astril Veliu回答的Kotlin版本

yourRecyclerView.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
            
            override fun onTouchEvent(view: RecyclerView, event: MotionEvent) {}
            
            override fun onInterceptTouchEvent(view: RecyclerView, event: MotionEvent): Boolean {
                when (event.action) {
                    MotionEvent.ACTION_DOWN -> {
                        yourRecyclerView.parent?.requestDisallowInterceptTouchEvent(true)
                    }
                }
                return false
            }
            
            override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
        })

2

我提出一个解决方案,参考Max Zonov的答案:

addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() {
                @Override
                public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
                    ViewParent vp = rv.getParent();
                    int action = e.getAction();
                    //check if can scroll horizontally both to right and to left
                    boolean tolLeft = rv.canScrollHorizontally(-1);
                    boolean tolRight = rv.canScrollHorizontally(1);
                    if (tolRight || tolLeft) {
                        if (action == MotionEvent.ACTION_MOVE) {
                            int historySize = e.getHistorySize();
                            boolean b = true;
                            if (historySize > 0) {
                                float x2 = e.getX();
                                float y2 = e.getY();
                                float x1 = e.getHistoricalX(0);
                                float y1 = e.getHistoricalY(0);
                                //check if user's touch is not going up or down the screen
                                //and if he/she's moving right if possible
                                //or left if possible
                                b = Math.abs(x2 - x1) > Math.abs(y2 - y1) &&
                                        (
                                                (tolLeft && (x2 - x1) > 0) ||
                                                        (tolRight && (x2 - x1) < 0)
                                        );
                            }
                            vp.requestDisallowInterceptTouchEvent(b);
                        }
                        return false;
                    }else {
                        if (action == MotionEvent.ACTION_MOVE)
                            vp.requestDisallowInterceptTouchEvent(false);
                        rv.removeOnItemTouchListener(this);
                        return true;
                    }
                }
            });

我刚刚添加了一些检查,否则(至少在我的情况下)嵌套的RecyclerView会停止"嵌套" RecyclerView的垂直滚动。此外,我认为最好也处理向后滚动,以防ViewPager的片段不是第一个。


1

View pager 和水平 recycler view 都可以水平滚动,因此操作系统很难找出要滚动哪一个。因此,您需要使用不可滑动的 ViewPager 替换第二个 ViewPager,在 this 答案中查看如何使用不可滑动的 ViewPager。


1
那会完全禁用ViewPager。而我不想这样。 - Rishabh876
1
不,您可以直接点击选项卡来切换选项卡。 - Suraj Vaishnav
1
我的意思是我需要手势起作用。它在RV2上运行,但在RV1上不起作用。所以我想知道我在这里漏掉了什么。 - Rishabh876

0

如果您不想在滚动到末尾后更改ViewPager片段,可以使用OhhhThatVarun的答案

如果您想在滚动到末尾后更改ViewPager片段:

recyclerView.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {

    override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
        val action = e.action
        if (recyclerView.canScrollHorizontally(RecyclerView.FOCUS_FORWARD) || binding.photos.canScrollHorizontally(-RecyclerView.FOCUS_FORWARD)) {
            when (action) {
               MotionEvent.ACTION_MOVE -> rv.parent
                        .requestDisallowInterceptTouchEvent(true)
            }
            return false
        }
        else {
            when (action) {
                MotionEvent.ACTION_MOVE -> rv.parent
                        .requestDisallowInterceptTouchEvent(false)
            }
            recyclerView.removeOnItemTouchListener(this)
            return true
        }
    }

    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}
    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
})

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