提问者:小点点

使用PagerSnapHelper的垂直和水平RecyclerViews


我正在试图找出实现两个列表的最佳方法,其中父列表可以垂直滑动,子列表可以水平滑动。父列表被假定为无限列表,而子列表最多可以有n个页面。回收站视图似乎是这项工作的最佳工具,并且考虑到PagerSnapHelper的添加,重新创建页面滑动行为真的很容易(想想ViewPage)。我目前面临的问题是,当我们水平滑动到结束或开始时,有时垂直回收站视图(父)会接管并更改页面。即使使用单个垂直回收站视图也可以重新创建,如下所示:

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

从上到下或从下到上滑动/翻转将按预期更改页面。然而,如果您进行水平运动(从左到右或从右到左),有时垂直滑动会启动。PagerSnapHelper似乎对快速移动非常敏感。当水平而不是垂直启动滑动时,有没有什么方法可以避免此页面更改?这个问题在我的例子中更明显,因为我也有一个水平的Pagersnaphelper。

可能的解决方案:

  • 扩展回收站查看控件onTouch事件和OnIntercepTouch事件。还没有想出使用它的方法。
  • 使用GestureDisector

如果需要,我很乐意提供更多的上下文/代码。拥有两个pagersnapper可能不是Android目前的模式,但即使我把那部分拿走,我也想知道为什么pagersnapper对某些手势如此敏感。


共2个答案

匿名用户

我认为,当必须在父级是垂直的情况下调用PagerSnapHelpers时,问题在于垂直父级会将一些水平滑动混淆为垂直滑动。为了避免这种混淆,我截取被检测为水平的事件,并且不允许父节点使用这种事件。以下是我所做的简化版:

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

    }
}

关键在于如何检测滚动是否应该被认为是水平,如在< code>return Math.abs(distanceX)中

匿名用户

apSTRK的静态编程语言类:

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