gpt4 book ai didi

android - 拖放 RecyclerView 的第一项会移动几个随机位置

转载 作者:行者123 更新时间:2023-12-04 23:48:48 32 4
gpt4 key购买 nike

目前,我有一个 RecyclerView 实现新的 ListAdapter,使用 submitList 来区分不同的元素并继续自动更新 UI。
最近我不得不使用众所周知的 ItemTouchHelper 实现拖放到列表。这是我的实现,非常简单:

class DraggableItemTouchHelper(private val adapter: DestinationsAdapter) : ItemTouchHelper.Callback() {
private val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
private val swipeFlags = 0

override fun isLongPressDragEnabled() = false
override fun isItemViewSwipeEnabled() = false

override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
return makeMovementFlags(dragFlags, swipeFlags)
}

override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val oldPos = viewHolder.adapterPosition
val newPos = target.adapterPosition

adapter.swap(oldPos, newPos)
return true
}

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
}
这是我在适配器内的交换功能:
fun swap(from: Int, to: Int) {
submitList(ArrayList(currentList).also {
it[from] = currentList[to]
it[to] = currentList[from]
})
}
除了移动列表的第一项时,一切都运行良好。有时它的行为还可以,但大多数时候(如 90%),即使将其移动到第二个项目的上方(例如将第一个项目移动到第二个位置),它也会捕捉多个位置。新职位似乎是随机的,我无法弄清楚问题所在。
作为指导,我使用了 https://github.com/material-components/material-components-android示例来实现拖放和他们的(简单)列表和布局效果很好。我的列表有点复杂,因为它在 viewpager 内,使用 Navigation 组件,并且在该屏幕中约束在一起的许多其他 View ,但我认为这不应该相关。
在这一点上,我什至不知道如何在网上搜索这个问题了。
我为此找到的最接近的解决方案可能是 https://issuetracker.google.com/issues/37018279但是在实现并具有相同的行为之后,我认为这是因为我使用不同的 ListAdapter 并异步更新列表,当解决方案使用 RecyclerView.Adapter 它使用 notifyItemMoved 和其他类似方法时。
切换到 RecyclerView.Adapter 不是解决方案。

最佳答案

这似乎是 AsyncListDiffer 中的一个错误, ListAdapter 在后台使用.我的解决方案可让您在需要时手动区分更改。但是,它相当 hacky,使用反射,并且可能不适用于 future appcompat版本(我测试过的版本是 1.3.0 )。
由于mDifferListAdapter 中是私有(private)的你需要直接使用它,你必须创建自己的ListAdapter实现(您可以复制原始来源)。然后添加以下方法:

fun setListWithoutDiffing(list: List<T>) {
setOf("mList", "mReadOnlyList").forEach { fieldName ->
val field = mDiffer::class.java.getDeclaredField(fieldName)
field.isAccessible = true
field.set(mDiffer, list)
}
}
此方法静默更改底层 AsyncListDiffer 中的当前列表。不触发任何差异,如 submitList()将。
生成的文件应如下所示:
package com.example.yourapp

import androidx.recyclerview.widget.AdapterListUpdateCallback
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.AsyncListDiffer.ListListener
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView

abstract class ListAdapter<T, VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH> {
private val mDiffer: AsyncListDiffer<T>
private val mListener =
ListListener<T> { previousList, currentList -> onCurrentListChanged(previousList, currentList) }

protected constructor(diffCallback: DiffUtil.ItemCallback<T>) {
mDiffer = AsyncListDiffer(
AdapterListUpdateCallback(this),
AsyncDifferConfig.Builder(diffCallback).build()
).apply {
addListListener(mListener)
}
}

protected constructor(config: AsyncDifferConfig<T>) {
mDiffer = AsyncListDiffer(AdapterListUpdateCallback(this), config).apply {
addListListener(mListener)
}
}

fun setListWithoutDiffing(list: List<T>) {
setOf("mList", "mReadOnlyList").forEach { fieldName ->
val field = mDiffer::class.java.getDeclaredField(fieldName)
field.isAccessible = true
field.set(mDiffer, list)
}
}

open fun submitList(list: List<T>?) {
mDiffer.submitList(list)
}

fun submitList(list: List<T>?, commitCallback: Runnable?) {
mDiffer.submitList(list, commitCallback)
}

protected fun getItem(position: Int): T {
return mDiffer.currentList[position]
}

override fun getItemCount(): Int {
return mDiffer.currentList.size
}

val currentList: List<T>
get() = mDiffer.currentList

open fun onCurrentListChanged(previousList: List<T>, currentList: List<T>) {}
}

现在您需要更改您的适配器实现以继承您的自定义 ListAdapter而不是 androidx.recyclerview.widget.ListAdapter .
最后,您需要更改适配器的 swap()使用 setListWithoutDiffing() 的方法实现和 notifyItemMoved()方法:
fun swap(from: Int, to: Int) {
setListWithoutDiffing(ArrayList(currentList).also {
it[from] = currentList[to]
it[to] = currentList[from]
})
notifyItemMoved(from, to)
}

另一种解决方案是创建自定义 AsyncListDiffer版本可以让你在没有反射的情况下做同样的事情,但这种方式似乎更容易。我还将提交功能请求以支持开箱即用的手动差异,并使用 Google 问题跟踪器链接更新问题。

关于android - 拖放 RecyclerView 的第一项会移动几个随机位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65804749/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com