android %2$s,android – 如何同步滚动的2个RecyclerViews的第...

背景

我有2个RecyclerView实例.一个是水平的,第二个是垂直的.

它们都显示相同的数据并且具有相同数量的项目,但是以不同的方式,并且单元格的大小不必相等.

我希望滚动到一个将与另一个同步,以便在一个上显示的第一个项目将始终显示在另一个上(作为第一个).

问题

即使我成功地使它们同步(我只选择哪一个是“主”,控制另一个的滚动),滚动的方向似乎影响它的工作方式.

假设片刻具有相同的高度:

如果我向上/向左滚动,它或多或少地按照我的意图工作:

6433eded3eab997e7942b27aabd75c69.png

但是,如果我向下/向右滚动,它会让其他RecyclerView显示另一个的第一项,但通常不会作为第一项:

b11484759636da811bb1dcec059812b6.png

注意:在上面的截图中,我已经在底部的RecyclerView中滚动,但是类似的结果将与顶部的一起.

如果像我写的那样,细胞有不同的大小,情况会变得更糟:

17cfe4740fdfc1182350c60554b1adb9.png

我试过的

我尝试使用其他滚动方式并转到其他位置,但所有尝试都失败了.

使用smoothScrollToPosition使事情变得更糟(虽然它看起来确实更好),因为如果我扔掉,在某些时候,其他RecyclerView控制我与之交互的那个.

我想我应该使用滚动的方向,以及其他RecyclerView上当前显示的项目.

这是当前(示例)代码.请注意,在实际代码中,单元格可能没有相同的大小(有些是高,有些是短等等).代码中的一行使单元格具有不同的高度.

activity_main.xml中

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: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: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"/>

horizo??ntal_cell.xml

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

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() {

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() {

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成为控件的问题?

编辑:在更新这里的示例代码后,具有不同大小的单元格(因为最初更接近我所拥有的问题,如前所述),我注意到捕捉不起作用.

这就是我选择使用此库正确捕捉它的原因:

因此,我使用’GravitySnapHelper’代替LinearSnapHelper.似乎工作得更好,但仍然有同步问题,并在滚动时触摸.

编辑:

我终于修复了所有同步问题,即使单元格大小不同,它也可以正常工作.

还有一些问题:

>如果您使用一个RecyclerView,然后触摸另一个,它会有非常奇怪的滚动行为.可能滚动的方式比它应该更多.

>滚动不平滑(同步时和投掷时),因此看起来不太好.

>遗憾的是,由于捕捉(我实际上可能只需要顶级的RecyclerView),它会导致另一个问题:底部的RecyclerView可能部分显示最后一项(截图有100个项目),我无法滚动更多以显示它完全:

c3590bfec16bb989acf19c974b061a3c.png

我甚至不认为底部的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() {

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() {

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)

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值