
背景
我有2个RecyclerVIEw实例.一个是水平的,第二个是垂直的.
它们都显示相同的数据并且具有相同数量的项目,但是以不同的方式,并且单元格的大小不必相等.
我希望滚动到一个将与另一个同步,以便在一个上显示的第一个项目将始终显示在另一个上(作为第一个).
问题
即使我成功地使它们同步(我只选择哪一个是“主”,控制另一个的滚动),滚动的方向似乎影响它的工作方式.
假设片刻具有相同的高度:
如果我向上/向左滚动,它或多或少地按照我的意图工作:
但是,如果我向下/向右滚动,它会让其他RecyclerVIEw显示另一个的第一项,但通常不会作为第一项:
注意:在上面的截图中,我已经在底部的RecyclerVIEw中滚动,但是类似的结果将与顶部的一起.
如果像我写的那样,细胞有不同的大小,情况会变得更糟:
我试过的
我尝试使用其他滚动方式并转到其他位置,但所有尝试都失败了.
使用smoothScrollToposition使事情变得更糟(虽然它看起来确实更好),因为如果我扔掉,在某些时候,其他RecyclerVIEw控制我与之交互的那个.
我想我应该使用滚动的方向,以及其他RecyclerVIEw上当前显示的项目.
这是当前(示例)代码.请注意,在实际代码中,单元格可能没有相同的大小(有些是高,有些是短等等).代码中的一行使单元格具有不同的高度.
activity_main.xml中
<androID.support.constraint.ConstraintLayout xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:app="http://schemas.androID.com/apk/res-auto" xmlns:tools="http://schemas.androID.com/tools" androID:layout_wIDth="match_parent" androID:layout_height="match_parent" tools:context=".MainActivity"> <androID.support.v7.Widget.RecyclerVIEw androID:ID="@+ID/topReccyclerVIEw" androID:layout_wIDth="0dp" androID:layout_height="100dp" androID:layout_marginEnd="8dp" androID:layout_marginStart="8dp" androID:layout_margintop="8dp" androID:orIEntation="horizontal" app:layoutManager="androID.support.v7.Widget.linearlayoutmanager" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constrainttop_totopOf="parent" tools:Listitem="@layout/horizontal_cell"/> <androID.support.v7.Widget.RecyclerVIEw androID:ID="@+ID/bottomrecyclerVIEw" androID:layout_wIDth="0dp" androID:layout_height="0dp" androID:layout_marginBottom="8dp" androID:layout_marginEnd="8dp" androID:layout_marginStart="8dp" androID:layout_margintop="8dp" app:layoutManager="androID.support.v7.Widget.linearlayoutmanager" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constrainttop_toBottomOf="@+ID/topReccyclerVIEw" tools:Listitem="@layout/horizontal_cell"/></androID.support.constraint.ConstraintLayout>horizontal_cell.xml
<TextVIEw androID:ID="@+ID/textVIEw" xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:tools="http://schemas.androID.com/tools" androID:layout_wIDth="100dp" androID:layout_height="100dp" androID:gravity="center" tools:text="@tools:sample/lorem"/>vertical_cell.xml
<TextVIEw androID:ID="@+ID/textVIEw" xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:tools="http://schemas.androID.com/tools" androID:layout_wIDth="match_parent" androID:layout_height="50dp" androID:gravity="center" tools:text="@tools:sample/lorem"/>主要活动
class MainActivity : AppCompatActivity() { var masterVIEw: VIEw? = null overrIDe fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentVIEw(R.layout.activity_main) val inflater = LayoutInflater.from(this) topReccyclerVIEw.adapter = object : RecyclerVIEw.Adapter<RecyclerVIEw.VIEwHolder>() { overrIDe fun onBindVIEwHolder(holder: RecyclerVIEw.VIEwHolder, position: Int) { (holder.itemVIEw as TextVIEw).text = position.toString() holder.itemVIEw.setBackgroundcolor(if(position%2==0) 0xffff0000.toInt() else 0xff00ff00.toInt()) } overrIDe fun getItemCount(): Int { return 100 } overrIDe fun onCreateVIEwHolder(parent: VIEwGroup?, vIEwType: Int): RecyclerVIEw.VIEwHolder { return object : RecyclerVIEw.VIEwHolder(inflater.inflate(R.layout.horizontal_cell, parent, false)) {} } } bottomrecyclerVIEw.adapter = object : RecyclerVIEw.Adapter<RecyclerVIEw.VIEwHolder>() { val baseHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50f, resources.displayMetrics).toInt() overrIDe fun onBindVIEwHolder(holder: RecyclerVIEw.VIEwHolder, position: Int) { (holder.itemVIEw as TextVIEw).text = position.toString() holder.itemVIEw.setBackgroundcolor(if(position%2==0) 0xffff0000.toInt() else 0xff00ff00.toInt()) // this makes the heights of the cells different from one another: holder.itemVIEw.layoutParams.height = baseHeight + (if (position % 3 == 0) 0 else baseHeight / (position % 3)) } overrIDe fun getItemCount(): Int { return 100 } overrIDe fun onCreateVIEwHolder(parent: VIEwGroup?, vIEwType: Int): RecyclerVIEw.VIEwHolder { return object : RecyclerVIEw.VIEwHolder(inflater.inflate(R.layout.vertical_cell, parent, false)) {} } } linearSnapHelper().attachToRecyclerVIEw(topReccyclerVIEw) linearSnapHelper().attachToRecyclerVIEw(bottomrecyclerVIEw) topReccyclerVIEw.addOnScrollListener(OnScrollListener(topReccyclerVIEw, bottomrecyclerVIEw)) bottomrecyclerVIEw.addOnScrollListener(OnScrollListener(bottomrecyclerVIEw, topReccyclerVIEw)) } inner class OnScrollListener(private val thisRecyclerVIEw: RecyclerVIEw, private val otherRecyclerVIEw: RecyclerVIEw) : RecyclerVIEw.OnScrollListener() { var lastItemPos: Int = Int.MIN_VALUE val thisRecyclerVIEwID = resources.getResourceEntryname(thisRecyclerVIEw.ID) overrIDe fun onScrollStateChanged(recyclerVIEw: RecyclerVIEw?, newState: Int) { super.onScrollStateChanged(recyclerVIEw, newState) Log.d("AppLog", "onScrollStateChanged:$thisRecyclerVIEwID $newState") when (newState) { RecyclerVIEw.SCRolL_STATE_DRAGGING -> if (masterVIEw == null) { Log.d("AppLog", "setting $thisRecyclerVIEwID to be master") masterVIEw = thisRecyclerVIEw } RecyclerVIEw.SCRolL_STATE_IDLE -> if (masterVIEw == thisRecyclerVIEw) { Log.d("AppLog", "resetting $thisRecyclerVIEwID from being master") masterVIEw = null lastItemPos = Int.MIN_VALUE } } } overrIDe fun onScrolled(recyclerVIEw: RecyclerVIEw?, dx: Int, dy: Int) { super.onScrolled(recyclerVIEw, dx, dy) if ((dx == 0 && dy == 0) || (masterVIEw != null && masterVIEw != thisRecyclerVIEw)) return // Log.d("AppLog", "onScrolled:$thisRecyclerVIEw $dx-$dy") val currentItem = (thisRecyclerVIEw.layoutManager as linearlayoutmanager).findFirstCompletelyVisibleItemposition() if (lastItemPos == currentItem) return lastItemPos = currentItem otherRecyclerVIEw.scrollToposition(currentItem)// otherRecyclerVIEw.smoothScrollToposition(currentItem) Log.d("AppLog", "currentItem:" + currentItem) } }}问题
>如何让其他RecycerVIEw始终拥有与当前控制的第一项相同的第一项?
>如何修改代码以支持平滑滚动,而不会导致突然让其他RecyclerVIEw成为控件的问题?
编辑:在更新这里的示例代码后,具有不同大小的单元格(因为最初更接近我所拥有的问题,如前所述),我注意到捕捉不起作用.
这就是我选择使用此库正确捕捉它的原因:
https://github.com/DevExchanges/SnappingRecyclerview
因此,我使用’GravitySnapHelper’代替linearSnapHelper.似乎工作得更好,但仍然有同步问题,并在滚动时触摸.
编辑:
我终于修复了所有同步问题,即使单元格大小不同,它也可以正常工作.
还有一些问题:
>如果您使用一个RecyclerVIEw,然后触摸另一个,它会有非常奇怪的滚动行为.可能滚动的方式比它应该更多.
>滚动不平滑(同步时和投掷时),因此看起来不太好.
>遗憾的是,由于捕捉(我实际上可能只需要顶级的RecyclerVIEw),它会导致另一个问题:底部的RecyclerVIEw可能部分显示最后一项(截图有100个项目),我无法滚动更多以显示它完全:
我甚至不认为底部的RecyclerVIEw应该是贴紧的,除非触顶了.可悲的是,这是我到目前为止所做的全部,没有同步问题.
在找到所有修复后,这是新代码:
class MainActivity : AppCompatActivity() { var masterVIEw: VIEw? = null overrIDe fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentVIEw(R.layout.activity_main) val inflater = LayoutInflater.from(this) topReccyclerVIEw.adapter = object : RecyclerVIEw.Adapter<RecyclerVIEw.VIEwHolder>() { overrIDe fun onBindVIEwHolder(holder: RecyclerVIEw.VIEwHolder, position: Int) { (holder.itemVIEw as TextVIEw).text = position.toString() holder.itemVIEw.setBackgroundcolor(if (position % 2 == 0) 0xffff0000.toInt() else 0xff00ff00.toInt()) } overrIDe fun getItemCount(): Int = 1000 overrIDe fun onCreateVIEwHolder(parent: VIEwGroup?, vIEwType: Int): RecyclerVIEw.VIEwHolder { return object : RecyclerVIEw.VIEwHolder(inflater.inflate(R.layout.horizontal_cell, parent, false)) {} } } bottomrecyclerVIEw.adapter = object : RecyclerVIEw.Adapter<RecyclerVIEw.VIEwHolder>() { val baseHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50f, resources.displayMetrics).toInt() overrIDe fun onBindVIEwHolder(holder: RecyclerVIEw.VIEwHolder, position: Int) { (holder.itemVIEw as TextVIEw).text = position.toString() holder.itemVIEw.setBackgroundcolor(if (position % 2 == 0) 0xffff0000.toInt() else 0xff00ff00.toInt()) holder.itemVIEw.layoutParams.height = baseHeight + (if (position % 3 == 0) 0 else baseHeight / (position % 3)) } overrIDe fun getItemCount(): Int = 1000 overrIDe fun onCreateVIEwHolder(parent: VIEwGroup?, vIEwType: Int): RecyclerVIEw.VIEwHolder { return object : RecyclerVIEw.VIEwHolder(inflater.inflate(R.layout.vertical_cell, parent, false)) {} } } // GravitySnapHelper is available from : https://github.com/DevExchanges/SnapPingRecyclervIEw GravitySnapHelper(Gravity.START).attachToRecyclerVIEw(topReccyclerVIEw) GravitySnapHelper(Gravity.top).attachToRecyclerVIEw(bottomrecyclerVIEw) topReccyclerVIEw.addOnScrollListener(OnScrollListener(topReccyclerVIEw, bottomrecyclerVIEw)) bottomrecyclerVIEw.addOnScrollListener(OnScrollListener(bottomrecyclerVIEw, topReccyclerVIEw)) } inner class OnScrollListener(private val thisRecyclerVIEw: RecyclerVIEw, private val otherRecyclerVIEw: RecyclerVIEw) : RecyclerVIEw.OnScrollListener() { var lastItemPos: Int = Int.MIN_VALUE val thisRecyclerVIEwID = resources.getResourceEntryname(thisRecyclerVIEw.ID) overrIDe fun onScrollStateChanged(recyclerVIEw: RecyclerVIEw?, newState: Int) { super.onScrollStateChanged(recyclerVIEw, newState) when (newState) { RecyclerVIEw.SCRolL_STATE_DRAGGING -> if (masterVIEw == null) { masterVIEw = thisRecyclerVIEw } RecyclerVIEw.SCRolL_STATE_IDLE -> if (masterVIEw == thisRecyclerVIEw) { masterVIEw = null lastItemPos = Int.MIN_VALUE } } } overrIDe fun onScrolled(recyclerVIEw: RecyclerVIEw?, dx: Int, dy: Int) { super.onScrolled(recyclerVIEw, dx, dy) if (dx == 0 && dy == 0 || masterVIEw !== null && masterVIEw !== thisRecyclerVIEw) { return } val otherLayoutManager = otherRecyclerVIEw.layoutManager as linearlayoutmanager val thisLayoutManager = thisRecyclerVIEw.layoutManager as linearlayoutmanager val currentItem = thisLayoutManager.findFirstCompletelyVisibleItemposition() if (lastItemPos == currentItem) { return } lastItemPos = currentItem otherLayoutManager.scrollTopositionWithOffset(currentItem, 0) } }}解决方法:
结合两个RecyclerVIEws,有四种运动情况:
一种.将水平回收器向左滚动
湾向右滚动它
C.将垂直回收器滚动到顶部
d.将其滚动到底部
案例a和c不需要照顾,因为它们开箱即用.对于案例b和d,你需要做两件事:
>了解您所处的回收器(垂直或水平)以及滚动的方向(向上或向下,向左或向右)和
>根据otherRecyclerVIEw中可见项目的数量计算(列表项目的)偏移量(如果屏幕较大,则偏移量也需要更大).
弄清楚这个有点繁琐,但结果非常直接.
@OverrIDe public voID onScrollStateChanged(RecyclerVIEw recyclerVIEw, int newState) { super.onScrollStateChanged(recyclerVIEw, newState); if (newState == RecyclerVIEw.SCRolL_STATE_DRAGGING) { if (masterVIEw == otherRecyclerVIEw) { thisRecyclerVIEw.stopScroll(); otherRecyclerVIEw.stopScroll(); syncScroll(1, 1); } masterVIEw = thisRecyclerVIEw; } else if (newState == RecyclerVIEw.SCRolL_STATE_IDLE && masterVIEw == thisRecyclerVIEw) { masterVIEw = null; } } @OverrIDe public voID onScrolled(RecyclerVIEw recyclervIEw, int dx, int dy) { super.onScrolled(recyclervIEw, dx, dy); if ((dx == 0 && dy == 0) || (masterVIEw != null && masterVIEw != thisRecyclerVIEw)) { return; } syncScroll(dx, dy); } voID syncScroll(int dx, int dy) { linearlayoutmanager otherLayoutManager = (linearlayoutmanager) otherRecyclerVIEw.getLayoutManager(); linearlayoutmanager thisLayoutManager = (linearlayoutmanager) thisRecyclerVIEw.getLayoutManager(); int offset = 0; if ((thisLayoutManager.getorIEntation() == HORIZONTAL && dx > 0) || (thisLayoutManager.getorIEntation() == VERTICAL && dy > 0)) { // scrolling horizontal recycler to left or vertical recycler to bottom offset = otherLayoutManager.findLastCompletelyVisibleItemposition() - otherLayoutManager.findFirstCompletelyVisibleItemposition(); } int currentItem = thisLayoutManager.findFirstCompletelyVisibleItemposition(); otherLayoutManager.scrollTopositionWithOffset(currentItem, offset); }当然,您可以将两个if子句组合在一起,因为这些子句是相同的.为了便于阅读,我认为将它们分开是件好事.
第二个问题是当“第一”回收器仍在滚动时触及相应的“其他”回收器时同步.以下代码(包括在上面)是相关的:
if (newState == RecyclerVIEw.SCRolL_STATE_DRAGGING) { if (masterVIEw == otherRecyclerVIEw) { thisRecyclerVIEw.stopScroll(); otherRecyclerVIEw.stopScroll(); syncScroll(1, 1); } masterVIEw = thisRecyclerVIEw;}触摸回收器并拖动一点时,newState等于SCRolL_STATE_DRAGGING.因此,如果在触摸相应的“其他”回收器之后触摸(并且拖动),则第二个条件(masterVIEw == otherRecyclervIEw)为真.然后停止两个回收器并且“其他”回收器与“此”回收器同步.
总结以上是内存溢出为你收集整理的android – 如何同步滚动的2个RecyclerViews的第一个位置?全部内容,希望文章能够帮你解决android – 如何同步滚动的2个RecyclerViews的第一个位置?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)