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);
}
}
}
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);
}
};
var touchedRVTag = -1
val itemTouchListener = object : RecyclerView.OnItemTouchListener {
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {}
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean
{
val rvTag = rv.tag as Int
if (touchedRVTag != -1 && touchedRVTag != rvTag) {
rvs[touchedRVTag].stopScroll()
}
touchedRVTag = rvTag
return false
}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
}
for (i in 0 until rvs.size) {
rvs[i].addOnItemTouchListener(itemTouchListener)
}
1.向每个回收视图添加相同的OnScrollListener以滚动其他回收视图
val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val currentRVTag = recyclerView.tag as Int
if (currentRVTag == touchedRVTag) {
for (rvTag in 0 until rvs.size) {
if (rvTag != currentRVTag) {
rvs[rvTag].scrollBy(dx, 0)
}
}
}
}
}
for (i in 0 until rvs.size) {
rvs[i].addOnScrollListener(scrollListener)
}
class ScrollSynchronizer {
val boundRecyclerViews: ArrayList<RecyclerView> = ArrayList()
var verticalOffset = 0
private val scrollListener = object: RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
// scroll every other rv to the same position
boundRecyclerViews.filter { it != recyclerView }.forEach { targetView ->
targetView.removeOnScrollListener(this)
targetView.scrollBy(dx, dy)
targetView.addOnScrollListener(this)
}
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
verticalOffset = recyclerView.computeVerticalScrollOffset()
}
}
}
fun add(view: RecyclerView) {
with (view) {
if (boundRecyclerViews.contains(view)) {
removeOnScrollListener(scrollListener)
boundRecyclerViews.remove(this)
}
addOnScrollListener(scrollListener)
boundRecyclerViews.add(this)
}
}
fun scrollToLastOffset(rv: RecyclerView) {
rv.removeOnScrollListener(scrollListener)
val currentOffset = rv.computeVerticalScrollOffset()
if (currentOffset != verticalOffset) {
rv.scrollBy(0, verticalOffset - currentOffset)
}
rv.addOnScrollListener(scrollListener)
}
fun disable() = boundRecyclerViews.forEach { it.removeOnScrollListener(scrollListener) }
fun enable() = boundRecyclerViews.forEach { it.addOnScrollListener(scrollListener) }
fun addAll(recyclerViews: List<RecyclerView>) = recyclerViews.forEach { add(it) }
}
编辑:按照@Groovee60的评论和建议,将onScrolled替换为:
// scroll every other rv to the same position
val otherRvs = boundRecyclerViews.filter { it != recyclerView }
otherRvs.forEach { targetView -> targetView.removeOnScrollListener(this) }
otherRvs.forEach { targetView -> targetView.scrollBy(dx, dy) }
otherRvs.forEach { targetView -> targetView.addOnScrollListener(this) }
9条答案
按热度按时间dgenwo3n1#
这是我的解决方案。代码越少越好...
lvDetail和lvDetail2是您要保持同步的循环视图。
uqjltbpv2#
我相信理解它的工作原理对您是有意义的,所以我将解释我设计解决方案时遵循的整个过程。注意,这个示例只针对两个RecyclerViews,但是使用更多的RecyclerViews就像使用一个RecyclerViews数组一样简单。
首先想到的是监听两个ScrollView上的滚动变化,当其中一个滚动时,在另一个上使用scrollBy(int x,int y)。不幸的是,编程式滚动也会触发监听器,因此最终会陷入循环。
为了克服这个问题,你需要设置一个OnItemTouchListener,当你点击RecyclerView时添加正确的ScrollListener,当滚动停止时删除它。这几乎可以完美地工作,但是如果你在一个长的RecyclerView中执行一个快速的扫视,然后在它完成之前再次滚动它,只有第一个滚动会被传输。
要解决此问题,您需要确保仅在RecyclerView空闲时添加OnScrollListener。
我们来看看来源:
这是你需要扩展OnScrollListeners的类,这样可以确保在需要时删除它们。
然后我有两个侦听器,每个侦听器对应一个RecyclerView:
然后在初始化时你可以设置OnItemTouchListeners。最好是为整个视图设置一个监听器,但是RecyclerView不支持这个。OnItemTouchListeners不会造成任何问题:
还要注意,在我的特定情况下,RecyclerViews不是第一个接收到touch事件的视图,因此我需要拦截它。如果不是您的情况,您可以(应该)将onInterceptTouchEvent(...)中的代码合并到onTouchEvent(...)中。
最后,如果用户试图同时滚动两个RecyclerViews,这将导致崩溃。这里最好的解决方案是在包含RecyclerViews的直接父对象中设置
android:splitMotionEvents="false"
。您可以看到代码here的示例。
r6l8ljro3#
我想我已经找到了一个简单明了的答案。
正如Jorge Antonio Díaz-Benito所说:"首先想到的是监听两个ScrollView上的滚动变化,当其中一个滚动时,在另一个上使用scrollBy(int x,int y)。不幸的是,通过编程滚动也会触发监听器,所以最终会陷入循环。"
所以你需要解决这个问题。如果你只是跟踪谁在滚动,他们就不会循环。
这是您的自定义OnScrollListener,用于检查scrollState是否为IDLE。这是否为真-〉没有人在滚动。因此'int viewIsScolling = -1
现在你需要到检测如果你能滚动.这是这代码:
viewIsScrolling =一个全局整数,开始时设置为-1;没有人在滚动的状态。你可以随意添加循环视图。
pu3pd22g4#
此解决方案处理fling。演示项目here
假设您有三个水平RecyclerView。
1.为每个RecyclerView添加标记。例如:
1.将相同的OnItemTouchListener添加到每个RecyclerView,以了解哪个RecyclerView处于活动状态。如果已经存在活动的RecyclerView,则对其调用stopScroll(如果活动的RecyclerView处于fling状态,则可防止不同步)
1.向每个回收视图添加相同的OnScrollListener以滚动其他回收视图
PS:如果你想滚动任何RecyclerView编程设置其标签,然后滚动:
s8vozzvw5#
Kotlin版本,这一个不处理投掷(尚未)
编辑:按照@Groovee60的评论和建议,将
onScrolled
替换为:jaxagkaj6#
首先考虑使用
NestedScrollView
作为RecyclerViews
的父对象,这可能需要一些额外的调整,但总体思路是相同的:如果你因为某些原因不能做到这一点,你可以通过编程来同步滚动。只是需要避免
onScrolled()
事件回调的无限循环,就像在其他答案中提到的那样。换句话说,当你通过编程开始滚动时,你在onScroll()
内部什么也不做,直到你通过编程完成滚动。并且不要通过编程滚动最初滚动的RecyclerView
。放入要同步的所有回收器
呼叫
好好享受
iswrvxsc7#
在寻呼机适配器中创建变量,该变量负责保存其页面的当前
scrollY
位置,无论何时getItem(position)
将调用更新ListView
位置,请查看https://github.com/ksoichiro/Android-ObservableScrollView/blob/master/samples/src/main/java/com/github/ksoichiro/android/observablescrollview/samples/ViewPagerTabActivity.java中的CacheFragmentStatePagerAdapter
uhry853o8#
我发现了一个非常简单的解决方案,它没有任何标志,而且非常动态
首先,您必须创建
RecyclerView.OnScrollListener
,并为该侦听器订阅您希望的每个水平布局。RecyclerView.OnScrollListener
:请注意RecyclerView必须具有相同的
layout.id
。在具有水平行的垂直RecyclerView的情况下非常有用qxgroojn9#
这个怎么样?