简体   繁体   English

带有 PagerSnapHelper 的垂直和水平 RecyclerViews

[英]Vertical and horizontal RecyclerViews with PagerSnapHelper

I'm trying to figure out what's the best way to implement two lists, where the parent list can be swiped vertically and the children can be swiped horizontally.我试图找出实现两个列表的最佳方法,其中父列表可以垂直滑动,子列表可以水平滑动。 Parent list is assumed to be an infinite list, while the children can have at most n pages.假设父列表是一个无限列表,而子列表最多可以有 n 页。 RecyclerView appears to be the best tool for the job and given the addition of PagerSnapHelper, it's really easy to recreate a page swipe behavior (think ViewPager.) The current problem I'm facing is that when the we horizontally swipe all the way to the end or the beginning, sometimes the vertical recyclerview (parent) takes over and changes page. RecyclerView 似乎是这项工作的最佳工具,并且考虑到 PagerSnapHelper 的添加,重新创建页面滑动行为真的很容易(想想 ViewPager。)我目前面临的问题是,当我们一直水平滑动到结束或开始,有时垂直回收站视图(父视图)接管并更改页面。 This can be recreated even with a single vertical recyclerview, as follows:即使使用单个垂直回收器视图也可以重新创建,如下所示:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);

    LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
    PagerSnapHelper snapHelper = new PagerSnapHelper();
    SearchAdapter searchAdapter = new SearchAdapter();

    mRecyclerView.setLayoutManager(layoutManager);
    mRecyclerView.setAdapter(searchAdapter);
    snapHelper.attachToRecyclerView(mRecyclerView);
}

A top to bottom or bottom to top swipe/fling will change pages as expected.从上到下或从下到上滑动/翻转将按预期更改页面。 However, if you do a horizontal motion (left to right or right to left) sometimes the vertical swipe kicks in. It seems like PagerSnapHelper is very sensitive to fast movements.但是,如果您进行水平运动(从左到右或从右到左),有时会出现垂直滑动。似乎 PagerSnapHelper 对快速移动非常敏感。 Is there any way to avoid this page changes when a swipe is initiated horizontally instead of vertically?当水平而不是垂直启动滑动时,有什么方法可以避免此页面更改? This issue is more noticeable in my case since I have a horizontal pagersnaphelper as well.这个问题在我的情况下更明显,因为我也有一个水平 pagersnaphelper。

Possible solutions:可能的解决方案:

  • Extend RecyclerView an control onTouchEvent and OnInterceptTouchEvent.扩展 RecyclerView 控件 onTouchEvent 和 OnInterceptTouchEvent。 Haven't figured out a way to use this.还没有想出一个方法来使用这个。
  • Use GestureDetector使用手势检测器

I'm happy to provide more context/code if needed.如果需要,我很乐意提供更多上下文/代码。 Having two PagerSnapHelper may not be a current pattern in Android, but even if I take that part away, I wonder why PagerSnapHelper is so sensitive to some gestures.拥有两个 PagerSnapHelper 在 Android 中可能不是当前模式,但即使我去掉那部分,我想知道为什么 PagerSnapHelper 对某些手势如此敏感。

I figured the issue when having to PagerSnapHelpers where the parent is vertical, was that the vertical parent was confusing some horizontal swipes as vertical.我发现在父垂直的 PagerSnapHelpers 时的问题是垂直父将一些水平滑动混淆为垂直。 In order to avoid such confusion I intercept event that are detected as horizontal and disallow the parent to consume such events.为了避免这种混淆,我拦截了检测为水平的事件,并禁止父级使用此类事件。 Here's a simplified version of what I did:这是我所做的简化版本:

public class VerticalRecyclerView extends RecyclerView {

    private GestureDetector mGestureDetector;

    public VerticalRecyclerView(Context context) {
        super(context);
        init();
    }

    public VerticalRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public VerticalRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        setLayoutManager(layoutManager);

        mGestureDetector = new GestureDetector(getContext(), new HorizontalScrollDetector());
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        if (mGestureDetector.onTouchEvent(e)) {
            return false;
        }
        return super.onInterceptTouchEvent(e);
    }

    public class HorizontalScrollDetector extends
            GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            return Math.abs(distanceX) > Math.abs(distanceY);
        }

    }
}

They key is on how to detect if a scroll should be consider horizontal, as in return Math.abs(distanceX) > Math.abs(distanceY);他们的关键是如何检测滚动是否应该被认为是水平的,作为return Math.abs(distanceX) > Math.abs(distanceY);

apSTRK's class in Kotlin: apSTRK 在 Kotlin 中的类:


class VerticalRecyclerView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {

    private var mGestureDetector = GestureDetector(context, object : SimpleOnGestureListener() {
        override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float)
            = abs(distanceX) > abs(distanceY)
    })

    override fun onInterceptTouchEvent(e: MotionEvent?) = if (mGestureDetector.onTouchEvent(e)) false
        else super.onInterceptTouchEvent(e)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM