java - 如何解决垂直 RecyclerView 内水平 ViewPager2 和 RecyclerView 的滚动问题?

标签 java android kotlin android-recyclerview android-viewpager2

我有一个 RecyclerView(例如 rootRecyclerView ),它可以根据某些 API 响应具有不同类型的行。我实现了其中一个是水平的ViewPager2另一种是通过水平 RecyclerView 实现的(例如,childRecyclerView)。

rootRecyclerView 垂直滑动,而 viewPager2 和 childRecyclerView 水平滑动。

问题:

当我在屏幕上滑动时,如果滑动在viewPager2上或childRecyclerView ,滑动必须水平完全笔直。否则,它们不会水平滚动;滑动由 rootRecyclerView 进行所以你会看到垂直运动。

所以,发生这种情况是因为您的拇指会沿弯曲/圆形方向移动,从而在 X 轴和 Y 轴上产生移动,因此 rootRecyclerView 会拦截滑动,从而产生这种不愉快的用户体验。

我确实尝试解决该问题,例如添加 OnItemTouchListener像这样的 childRecyclerView:

    private float Y_BUFFER = ViewConfiguration.get(getContext())
                                     .getScaledPagingTouchSlop(); // 10;
    private float preX = 0f;
    private float preY = 0f;
childRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
                if(e.getAction()==MotionEvent.ACTION_DOWN){
                    childRecyclerView.getParent().requestDisallowInterceptTouchEvent(true);
                }
                if(e.getAction() == MotionEvent.ACTION_MOVE){
                    if (Math.abs(e.getX() - preX) > Math.abs(e.getY() - preY)) {
                        childRecyclerView.getParent().requestDisallowInterceptTouchEvent(true);
                    } else if (Math.abs(e.getY() - preY) > Y_BUFFER) {
                        childRecyclerView.getParent().requestDisallowInterceptTouchEvent(false);
                    }
                }
                preX = e.getX();
                preY = e.getY();
                return false;
            }
// ... rest of the code

它仅解决 childRecyclerView 的问题,但我无法解决ViewPager2 .

我也尝试过使用GestureDetector如本answer link中所述,以及其他一些代码组合,但我无法使其工作。

有人可以帮助我吗?

最佳答案

好吧,经过一番研究,我得出的结论是替换我的 ViewPager2recyclerView这将“表现得像”viewPager :/.

首先我更换了我的 viewPager2与水平recyclerView 。要使其表现得像一个viewpager,请使用 SnapHelper .

RecyclerView childRecyclerView2 = findViewById(R.id.previously_viewPager);
// other init like setup layout manager, adapter etc 
SnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(replacedRecyclerView); // <-- this makes out rv behave like a viewPager

之后,您必须添加 OnItemTouchListener并覆盖onInterceptTouchEvent就像我问题中的代码段:

childRecyclerView2.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
// same as the code segment in the question, 
//so skipping this part. 
//just copy it from my question
          }
// ...
}

可选:

在viewPager2中,可以通过 getCurrentItem() 获取当前焦点,但是由于我们已经用recyclerview替换了viewpager2,所以我们没有这个方法。因此,我们需要实现我们自己的等效版本。如果你是 Kotlin 爱好者,可以直接跳到 reference 2并跳过这部分。如果您需要的话,这里是 java 版本,不过我会跳过解释。 创建SnapHelperExt.java

public class SnapHelperExt {

    public SnapHelperExt(){}

    public int getSnapPosition(RecyclerView recyclerView, SnapHelper snapHelper){
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        View snapView =  snapHelper.findSnapView(layoutManager);
        if (snapView != null) {
            return layoutManager.getPosition(snapView);
        }else{
            return -1;
        }
    }
}

接下来创建一个接口(interface)OnSnapPositionChangeListener作为我们的听众:

public interface OnSnapPositionChangeListener {
    void onSnapPositionChange(int position);
}

之后,创建 SnapOnScrollListener.java :

public class SnapOnScrollListener extends RecyclerView.OnScrollListener {

    public enum Behavior {
        NOTIFY_ON_SCROLL,
        NOTIFY_ON_SCROLL_STATE_IDLE
    }

    private SnapHelperExt snapHelperExt;
    private SnapHelper snapHelper;
    private Behavior behavior;
    private OnSnapPositionChangeListener onSnapPositionChangeListener;
    private int snapPosition = RecyclerView.NO_POSITION;


    public SnapOnScrollListener(SnapHelper snapHelper, Behavior behavior, OnSnapPositionChangeListener onSnapPositionChangeListener){
        this.snapHelper = snapHelper;
        this.behavior = behavior;
        this.onSnapPositionChangeListener = onSnapPositionChangeListener;
        this.snapHelperExt = new SnapHelperExt();
    }

    @Override
    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        if (behavior == Behavior.NOTIFY_ON_SCROLL) {
            maybeNotifySnapPositionChange(recyclerView);
        }
    }

    @Override
    public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        if (behavior == Behavior.NOTIFY_ON_SCROLL_STATE_IDLE
                && newState == RecyclerView.SCROLL_STATE_IDLE) {
            maybeNotifySnapPositionChange(recyclerView);
        }
    }

    private void maybeNotifySnapPositionChange(RecyclerView recyclerView){
        int prevPosition = this.snapHelperExt.getSnapPosition(recyclerView, snapHelper);
        boolean snapPositionIsChanged = (this.snapPosition!=prevPosition);

        if(snapPositionIsChanged){
            onSnapPositionChangeListener.onSnapPositionChange(prevPosition);
            this.snapPosition = prevPosition;
        }
    }
}

最后,这样使用:

            SnapOnScrollListener snapOnScrollListener = new SnapOnScrollListener(
                    snapHelper,
                    SnapOnScrollListener.Behavior.NOTIFY_ON_SCROLL,
                    position -> {   
Log.e(TAG, "currently focused page no = "+position);    
// your code here, do whatever you want
}
            );

            childRecyclerView2.addOnScrollListener(snapOnScrollListener);

引用文献:

  1. create-viewpager-using-recyclerview
  2. detecting-snap-changes-with-androids-recyclerview

关于java - 如何解决垂直 RecyclerView 内水平 ViewPager2 和 RecyclerView 的滚动问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60313270/

相关文章:

Java 变量可见性

java - 桌面 java 应用程序通过 USB 复制和传输 android 数据

java - 如何使用 C++ dlopen 宏从 C++ 代码而不是 JAVA 加载库

php - 将 Firebase Cloud Messaging 发送到手机设备

android - 在整洁架构的上下文中,用例和交互器意味着什么?

android - 亚行将不会运行

java - 在Java中播放时将mp3文件写入磁盘

kotlin - 将 spring-data-mongodb 与 Kotlin 一起使用时出现异常

json - 如何将 JSON 字符串映射到 Kotlin Map

kotlin - 如何在 ktor-client 中禁用重定向