多RecyclerViews的同步滚动 [英] Sync scrolling of multiple RecyclerViews

查看:1255
本文介绍了多RecyclerViews的同步滚动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个ViewPager每页显示的片段。该片段拥有RecyclerView内的项目的列表。项目的列表总是相同的尺寸和的各项的观点是相同的高度也。当滚动RecyclerViews之一,我想其他RecyclerViews在同一时间和同一距离滚动。我将如何同步RecyclerViews的滚动?

I have a ViewPager showing a fragment per page. This fragment holds a list of items inside a RecyclerView. The list of items is always the same size and the views for the items are also of the same height. When scrolling one of the RecyclerViews, I want the other RecyclerViews to scroll at the same time and the same distance. How would I synchronise the scrolling of the RecyclerViews?

推荐答案

我相信这是有关你了解它的工作原理,所以我要解释一下,我跟着来设计我的解决方案的整个过程。注意,这个例子是对于只有两个RecyclerViews,但更多的这样做是由于使用RecyclerViews的阵列一样容易。

I believe it is relevant for you to understand its workings, so I am going to explain the whole procedure that I followed to design my solution. Note that this example is for only two RecyclerViews, but doing it with more is as easy as using an array of RecyclerViews.

这想到的是监听两个ScrollViews滚动变化,当他们中的一个卷轴,使用<一首选项href=\"https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#scrollBy(int,%20int)\"相对=nofollow> scrollBy(INT X,int y)对上另一个。不幸的是,编程滚动也会触发听者,所以你会在一个循环结束了。

The first option that comes to mind is listening for scroll changes on both ScrollViews and, when one of them scrolls, use scrollBy(int x, int y) on the other one. Unfortunately, programmatically scrolling will also trigger the listener, so you'll end up in a loop.

要解决这个问题,你将需要设置,增加了当RecyclerView被触摸适当ScrollListener的OnItemTouchListener和滚动停止时删除它。这个作品几乎完美,但如果你执行长RecyclerView快速一扔,然后再滚动它完成之前,只有第一个滚动将被转移。

To overcome this problem, you will need to setup an OnItemTouchListener that adds the proper ScrollListener when a RecyclerView is touched, and removes it when the scrolling stops. This works almost flawlessly, but if you perform a quick fling in a long RecyclerView, and then scroll it again before it finishes, only the first scroll will be transferred.

要解决这个问题,你需要确保OnScrollListener仅添加当RecyclerView处于闲置状态。

To work around this, you will need to ensure that the OnScrollListener is only added when the RecyclerView is idle.

让我们来看看源:

    public class SelfRemovingOnScrollListener extends RecyclerView.OnScrollListener {

    @Override
    public final void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            recyclerView.removeOnScrollListener(this);
        }
    }
}

这是你需要扩展OnScrollListeners类。这可确保在需要时,它们会被删除。

This is the class from which you need to extend your OnScrollListeners. This ensures that they are removed when needed.

然后我有两个监听器,每个RecyclerView:

Then I have the two listeners, one for each RecyclerView:

private final RecyclerView.OnScrollListener mLeftOSL = new SelfRemovingOnScrollListener() {
    @Override
    public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
        super.onScrolled(recyclerView, dx, dy);
        mRightRecyclerView.scrollBy(dx, dy);
    }
}, mRightOSL = new SelfRemovingOnScrollListener() {

    @Override
    public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
        super.onScrolled(recyclerView, dx, dy);
        mLeftRecyclerView.scrollBy(dx, dy);
    }
};

然后在初始化时,你可以设置OnItemTouchListeners。这将是更好地建立整个视图中的单个监听器代替,但RecyclerView不支持这一点。 OnItemTouchListeners不反正构成问题:

And then upon initialization you can setup the OnItemTouchListeners. It would be better to set up a single listener for the whole view instead, but RecyclerView does not support this. OnItemTouchListeners don't pose a problem anyway:

    mLeftRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {

        private int mLastY;

        @Override
        public boolean onInterceptTouchEvent(@NonNull final RecyclerView rv, @NonNull final
        MotionEvent e) {
            Log.d("debug", "LEFT: onInterceptTouchEvent");

            final Boolean ret = rv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE;
            if (!ret) {
                onTouchEvent(rv, e);
            }
            return Boolean.FALSE;
        }

        @Override
        public void onTouchEvent(@NonNull final RecyclerView rv, @NonNull final MotionEvent e) {
            Log.d("debug", "LEFT: onTouchEvent");

            final int action;
            if ((action = e.getAction()) == MotionEvent.ACTION_DOWN && mRightRecyclerView
                    .getScrollState() == RecyclerView.SCROLL_STATE_IDLE) {
                mLastY = rv.getScrollY();
                rv.addOnScrollListener(mLeftOSL);
            }
            else {
                if (action == MotionEvent.ACTION_UP && rv.getScrollY() == mLastY) {
                    rv.removeOnScrollListener(mLeftOSL);
                }
            }
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(final boolean disallowIntercept) {
            Log.d("debug", "LEFT: onRequestDisallowInterceptTouchEvent");
        }
    });

    mRightRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {

        private int mLastY;

        @Override
        public boolean onInterceptTouchEvent(@NonNull final RecyclerView rv, @NonNull final
        MotionEvent e) {
            Log.d("debug", "RIGHT: onInterceptTouchEvent");

            final Boolean ret = rv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE;
            if (!ret) {
                onTouchEvent(rv, e);
            }
            return Boolean.FALSE;
        }

        @Override
        public void onTouchEvent(@NonNull final RecyclerView rv, @NonNull final MotionEvent e) {
            Log.d("debug", "RIGHT: onTouchEvent");

            final int action;
            if ((action = e.getAction()) == MotionEvent.ACTION_DOWN && mLeftRecyclerView
                    .getScrollState
                            () == RecyclerView.SCROLL_STATE_IDLE) {
                mLastY = rv.getScrollY();
                rv.addOnScrollListener(mRightOSL);
            }
            else {
                if (action == MotionEvent.ACTION_UP && rv.getScrollY() == mLastY) {
                    rv.removeOnScrollListener(mRightOSL);
                }
            }
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(final boolean disallowIntercept) {
            Log.d("debug", "RIGHT: onRequestDisallowInterceptTouchEvent");
        }
    });
}

还要注意的是,在我的特殊情况下,RecyclerViews并不是第一批接收触摸事件,所以我需要拦截。如果这不是你的情况,你可以(应该)合并来自onInterceptTouchEvent(...)的code到onTouchEvent(...)。

Note also that, in my particular case, the RecyclerViews are not the first ones to receive the touch event, so I need to intercept it. If this is not your case, you can (should) merge the code from onInterceptTouchEvent(...) into onTouchEvent(...).

最后,如果用户试图在同一时间滚动你的两个RecyclerViews这将导致崩溃。尽最大努力品质的解决方案可能在这里是设置的android:splitMotionEvents =FALSE在包含RecyclerViews直接父

Finally, this will cause a crash if your user attempts to scroll your two RecyclerViews at the same time. The best effort-quality solution possible here is to set android:splitMotionEvents="false" in the direct parent containing the RecyclerViews.

您可以看到与此code rel=\"nofollow\">一个例子。

You can see an example with this code here.

这篇关于多RecyclerViews的同步滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆