我正在尝试将Selection(https://developer.android.com/reference/androidx/recyclerview/selection/package-summary)与新的分页库(所以分页3)一起使用
使用 Paging 2 是可行的,因为我们使用的是 PagedListAdapter,并且它与 Selection 库一起工作,但现在使用新的 PagingDataAdapter 我无法再让它工作了。
https://developer.android.com/reference/kotlin/androidx/paging/PagingDataAdapter
我们正在失去 getCurrentList(),setHasStableIds() 现在将返回 UnsupportedOperationException。
因此,如果有人正在使用它,我将非常感谢对此的帮助。
在新的 Paging3 库中,您可以使用
snapShot()
类的 PagingDataAdapter
方法获取当前列表。
我最近遇到了同样的密钥提供程序问题,因此我实现了一个自定义密钥提供程序,如下所示:
class MyItemKeyProvider(private val adapter: MyPagingAdapter) :
ItemKeyProvider<String>(SCOPE_CACHED) {
override fun getKey(position: Int): String = adapter.snapshot().items[position].id
override fun getPosition(key: String): Int = adapter.snapshot().items.indexOfFirst { it.id == key }
如果您已经将其与 Paging 2 一起使用,我建议您首先实现您自己的
ItemKeyProvider<K>
。打包的 StableIdKeyProvider
无法使用,因为它需要启用稳定 ID,并且正如您所说,PagingDataAdapter
不支持稳定 ID。
Paging v3 无法与开箱即用的 recyclerview-selection 一起使用,因为选择库确实使用稳定的 ID,但 Paging v3 不支持它们。
为了使它们相互协作,您需要实现自己的稳定 id 密钥提供程序。一旦项目绑定到回收器视图,它就会保存项目 ID。
class MyStableIdKeyProvider(
private val mRecyclerView: RecyclerView
) : ItemKeyProvider<Long>(SCOPE_CACHED) {
init {
mRecyclerView.addOnChildAttachStateChangeListener(
object : RecyclerView.OnChildAttachStateChangeListener {
override fun onChildViewAttachedToWindow(view: View) {
onAttached(view)
}
override fun onChildViewDetachedFromWindow(view: View) {
onDetached(view)
}
}
)
}
private val mPositionToKey = mutableMapOf<Int, Long>()
private val mKeyToPosition = mutableMapOf<Long, Int>()
fun /* synthetic access */onAttached(view: View) {
val holder = mRecyclerView.findContainingViewHolder(view)
if (holder == null) {
Log.w(TAG, "Unable to find ViewHolder for View. Ignoring onAttached event.")
return
}
val position = holder.bindingAdapterPosition
val myItemId = (holder as ItemVH).myItemId
if (position != RecyclerView.NO_POSITION && myItemId != null) {
mPositionToKey[position] = myItemId
mKeyToPosition[myItemId] = position
}
}
fun /* synthetic access */onDetached(view: View) {
val holder = mRecyclerView.findContainingViewHolder(view)
if (holder == null) {
Log.w(TAG, "Unable to find ViewHolder for View. Ignoring onDetached event.")
return
}
val position = holder.bindingAdapterPosition
val myItemId = (holder as ItemVH).myItemId
if (position != RecyclerView.NO_POSITION && myItemId != null) {
mPositionToKey.remove(position)
mKeyToPosition.remove(myItemId)
}
}
override fun getKey(position: Int): Long? = mPositionToKey[position]
override fun getPosition(key: Long): Int = mKeyToPosition[key] ?: RecyclerView.NO_POSITION
companion object {
private const val TAG = "MyKeyProvider"
}
}
您的 ItemVH 必须提供其 ID。您不能使用 getItemId(),因为它总是返回 NO_ID。
class ItemVH(
view: View,
selectionTracker: SelectionTracker<Long>,
) : RecyclerView.ViewHolder(view) {
protected var myItem: MyItem? = null
val myItemId: Long? get() = myItem?.id
fun bind(myItem: MyItem, position: Int) {
this.myItem = myItem
// Put data to item's views
// ..
// Update checkmark visibility passing `myItemId` to the selection tracker
if (selectionTracker.isSelected(myItemId)) {
selectedView.visibility = View.VISIBLE
} else {
selectedView.visibility = View.GONE
}
}
fun getItemDetails() = object: ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition() = bindingAdapterPosition
override fun getSelectionKey() = myItemId
}
fun clear() {
myItem = null
}
}
适配器非常简单
class MyItemListAdapter : PagingDataAdapter<MyItem, ItemVH>(DIFF_CALLBACK) {
lateinit var selectionTracker: SelectionTracker<Long>
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemVH {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
return ItemVH(view, selectionTracker)
}
override fun onBindViewHolder(viewHolder: ItemVH, position: Int) {
val myItem = getItem(position)
if (myItem == null) {
viewHolder.clear()
} else {
viewHolder.bind(myItem, position)
}
}
companion object {
private val DIFF_CALLBACK = object :
DiffUtil.ItemCallback<MyItem>() {
// ...
}
}
}
构建选择跟踪器需要查找项目详细信息:
class ItemListLookup(
private val rv: RecyclerView
) : ItemDetailsLookup<Long>() {
override fun getItemDetails(e: MotionEvent): ItemDetails<Long>? {
val view = rv.findChildViewUnder(e.x, e.y)
if(view != null) {
return (rv.getChildViewHolder(view) as ItemVH)
.getItemDetails()
}
return null
}
}
将碎片全部连接在一起
class MyFragment : Fragment() {
private val adapter = MyItemListAdapter()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.recyclerView.adapter = adapter
adapter.selectionTracker = SelectionTracker.Builder<Long>(
"item_selection",
view.recyclerView,
MyStableIdKeyProvider(view.recyclerView),
ItemListLookup(view.recyclerView),
StorageStrategy.createLongStorage()
).build()
adapter.selectionTracker.addObserver(object : SelectionTracker.SelectionObserver<Long>() {
override fun onItemStateChanged(key: Long, selected: Boolean) {
// handle selection change event
}
override fun onSelectionCleared() {
// clear selection
}
})
// ...
}
}