当子RecyclerView滚动到最后一个项目时禁用ViewPager翻页。

14

我有一个viewpager,在其中一个片段中,我有两个包含垂直和水平recyclerview的不同片段。

当我将水平recyclerview滚动到最后一个项目并尝试继续滑动时,viewpager会滚动到下一页。我不希望这种情况发生。我想在尝试overscroll水平recyclerview时禁用viewpager的分页。

但是,我不希望在其他任何位置滑动时禁用viewpager的分页。例如,如果我在垂直recyclerview或父片段中的任何空白处滑动,则仍应导致viewpager更改页面。

我在此SO问题中阅读了如何禁用viewpager的分页。此外,此SO问题也类似于有一个子viewpager,但是我尝试使用水平recyclerview复制它时没有成功。

以下是一些结构:

自定义的viewpager,允许我禁用分页(从上面的第一个SO链接中获取):

public class CustomViewPager extends ViewPager {

private boolean enabled;

public CustomViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.enabled = true;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (this.enabled) {
        return super.onTouchEvent(event);
    }

    return false;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    if (this.enabled) {
        return super.onInterceptTouchEvent(event);
    }

    return false;
}

public void setPagingEnabled(boolean enabled) {
    this.enabled = enabled;
} }

对于水平的RecyclerView,我设置了一个ontouchlistener(类似上面第二个SO链接):

horizontalRecyclerView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    switch(event.getAction()){
                        case MotionEvent.ACTION_MOVE:
                            customViewPager.setPagingEnabled(false);
                            break;
                        case MotionEvent.ACTION_UP:
                            customViewPager.setPagingEnabled(true);
                            break;
                    }
                    return false;
                }
            });

额外观察:我注意到有时如果我在滑动之前长按水平recyclerview,它不会进入viewpager的下一页。但是,如果我快速滑动,viewpager就会进入下一页。

有人知道正确的方法吗?

感谢任何帮助。


找到任何解决方案,请告诉我。我正在寻找相同的东西。 - Shabbir Dhangot
遇到了类似的问题。如果有任何解决方法,请帮忙一下。先谢谢了! - Jatin
4个回答

32

这是解决方案。

requestDisallowInterceptTouchEvent()

这种方法将不允许父元素移动。它会停止viewpager触摸事件。

horizontalRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            int action = e.getAction();
            switch (action) {
                case MotionEvent.ACTION_MOVE:
                    rv.getParent().requestDisallowInterceptTouchEvent(true);
                    break;
            }
            return false;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        }
    });

这个程序可以正常运行,但是RecyclerView滚动到了最后一项,而应该显示第一项。 - Ram Mandal
1
我不得不使用request...来防止所有事件的行为。public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { rv.getParent().requestDisallowInterceptTouchEvent(true); return false; } - Jakub Adamek
它正在工作...但是在触摸horizontalRecyclerView时,垂直滚动不起作用。有什么解决办法吗? - Rathiga Jesika
1
不阻止垂直滚动添加:boolean isVerticalScroll = Math.abs(dy) > Math.abs(dx); if (!isVerticalScroll){rv.getParent().requestDisallowInterceptTouchEvent(true)}. 如何获取dy和dx的逻辑可以在RecyclerView源代码中找到。 - Alexey
@Alexey,您能否提供一小段代码展示您确切添加了这段代码的位置以及如何计算dx和dy的值? - Thanasis M

1
为了解决竖向滚动不再工作的问题,我做了如下处理:
为RecyclerView项创建一个触摸监听器。
mGestureDetector = new GestureDetector(itemView.getContext(), new GestureListener());

/**
 * Prevents the view pager from switching tabs when scrolling the carousel
 */
private class TouchListener implements RecyclerView.OnItemTouchListener {

  @Override
  public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
      mGestureDetector.onTouchEvent(e);
      return false;
  }
}

实现一个GestureListener,在检测到水平滚动时禁止触摸事件:

private class GestureListener extends SimpleOnGestureListener {
    private final int Y_BUFFER = 10;

    @Override
    public boolean onDown(MotionEvent e) {
        // Prevent ViewPager from intercepting touch events as soon as a DOWN is detected.
        // If we don't do this the next MOVE event may trigger the ViewPager to switch
        // tabs before this view can intercept the event.
        mRecyclerView.requestDisallowInterceptTouchEvent(true);
        return super.onDown(e);
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        if (Math.abs(distanceX) > Math.abs(distanceY)) {
            // Detected a horizontal scroll, prevent the viewpager from switching tabs
            mRecyclerView.requestDisallowInterceptTouchEvent(true);
        } else if (Math.abs(distanceY) > Y_BUFFER) {
            // Detected a vertical scroll of large enough magnitude so allow the the event
            // to propagate to ancestor views to allow vertical scrolling.  Without the buffer
            // a tab swipe would be triggered while holding finger still while glow effect was
            // visible.
            mRecyclerView.requestDisallowInterceptTouchEvent(false);
        }
        return super.onScroll(e1, e2, distanceX, distanceY);
    }
}

1

万一有人遇到和我同样的问题。

如果您的层次结构中有一个SwipeRefreshLayout,并且它的子项确实返回ViewCompat.isNestedScrollEnabled()(在我的情况下是FrameLayout),则这将不起作用,因为在传递requestDisallowInterceptTouchEvent()之前,SwipeRefreshLayout会进行检查。

花了我一些时间来调试,但这里是SwipeRefreshLayout的具体检查:

if ((VERSION.SDK_INT >= 21 || !(this.mTarget instanceof AbsListView)) && (this.mTarget == null || ViewCompat.isNestedScrollingEnabled(this.mTarget))) {
    super.requestDisallowInterceptTouchEvent(b);
}

通过移除 FrameLayout,将 SwipeRefreshLayout 的子项替换为 NestedScrollView,我成功地使用了 Shabbir Dhangot 的答案。

0

对于 Kotlin,您可以尝试这个解决方案

 horizontalRecyclerView.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
        override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
            val action = e.action
            when (action) {
                MotionEvent.ACTION_MOVE -> rv.parent.requestDisallowInterceptTouchEvent(true)
            }
            return false
        }

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

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