您好!有没有人实现了
RecyclerView Adapter
,它通过 Filter 实现过滤,并且可以通过有效负载更新其他项目?当您想要更新一个元素,但已经使用了过滤时,如何同步两个列表(原始列表和过滤后的列表)?有谁知道有什么好的例子吗?
PS:最重要的是,适配器中有很多元素,并且通过
AsyncListDiffer
进行更新。
提前感谢大家的回答!
主要问题是,当我在搜索中输入查询时,我会得到过滤的项目,将其中一个项目添加到收藏夹(在项目中标有星号),但是当我清除搜索时,列表项目不是最喜欢的(星号不显示)。一切也变得复杂,如何在
AsyncListDiffer
期间正确更新列表元素,以免在列表异步更新时意外更新它们。
我怀疑问题出在
submitList
和updateItem
方法上:
class Adapter(
private val listener: AdapterListener
): RecyclerView.Adapter<RecyclerView.ViewHolder>(), Filterable {
private val diffCallback = object : DiffUtil.ItemCallback<UiModel>() {
override fun areItemsTheSame(oldItem: UiModel, newItem: UiModel): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: UiModel, newItem: UiModel): Boolean {
return (
oldItem.name == newItem.name &&
oldItem.id == newItem.id &&
oldItem.isFavorite == newItem.isFavorite
)
}
}
private var listDiffer = AsyncListDiffer(this, diffCallback)
private val initialDataItems = mutableListOf<UiModel>()
private val dataFilter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val queryRawName = constraint?.toString()
if (queryRawName.isNullOrEmpty()) return FilterResults().apply { values = initialDataItems }
val queryName = queryRawName.lowercase().trim()
val filteredItems = initialDataItems.filter { it.name?.lowercase()?.contains(queryName).isTrue() }
return FilterResults().apply { values = filteredItems }
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
val values = results?.values as? List<*> ?: return
val filteredValues = values.filterIsInstance(UiModel::class.java)
if (values.size != filteredValues.size) return
listDiffer.submitList(filteredValues)
}
}
override fun getItemCount() = listDiffer.currentList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = parent.inflate(R.layout.item_layout)
return CustomViewHolder(
view = view,
listener = { listener.onClickFavorite(uiModel) }
)
}
override fun onBindViewHolder(
holder: RecyclerView.ViewHolder,
position: Int,
payloads: MutableList<Any>
) {
val payloadValid = payloads.isNotEmpty() && payloads[0] is UiModel
if (payloadValid && holder is CustomViewHolder) {
holder.updatePayload(payloads[0] as UpdateUiModel)
} else {
super.onBindViewHolder(holder, position, payloads)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = getItem(position)
(holder as? CustomViewHolder)?.bind(item)
}
override fun getFilter() = dataFilter
fun submitList(data: List<UiModel>, updateInitialItems: Boolean = true) {
listDiffer.submitList(data)
if (updateInitialItems) {
initialDataItems.clear()
initialDataItems.addAll(data)
}
}
fun updateItem(payload: UpdateUiModel) {
if (payload.id == null) return
listDiffer.currentList.forEachIndexed { index, dataItem ->
if (dataItem.id == payload.id) {
updateModel(index, payload)
notifyItemChanged(index, payload)
}
}
}
private fun isCorrectPosition(position: Int) = position in 0 until itemCount
private fun updateModel(index: Int, payload: UiModel) {
val currentList = listDiffer.currentList.toMutableList()
if (isCorrectPosition(index)) currentList[index] = currentList[index].updateModel(payload)
submitList(currentList)
}
private fun getItem(position: Int) = listDiffer.currentList[position]
}
是的,确实需要更新初始列表。我改变了什么:
submitList
方法:
fun submitList(data: List<UiModel>) {
listDiffer.submitList(data)
initialDataItems.clear()
initialDataItems.addAll(data)
}
updateItem
方法(代码可以改进,我写得很快):
fun updateItem(payload: UpdateUiModel) {
if (payload.id == null) return
listDiffer.currentList.forEachIndexed { index, dataItem ->
if (dataItem.id == payload.id) {
updateModel(index, payload)
}
}
val newInitialList = initialDataItems.toMutableList()
initialDataItems.forEachIndexed { index, dataItem ->
if (dataItem.id == payload.id) {
if (isCorrectPosition(index)) newInitialList[index] = newInitialList[index].updateModel(payload)
}
}
initialDataItems = newInitialList
}
请注意,最后我这样做了
initialDataItems = newInitialList
,以便完全更新初始列表。
最后一个方法:
private fun updateModel(index: Int, payload: UiModel) {
val currentList = listDiffer.currentList.toMutableList()
if (isCorrectPosition(index)) currentList[index] = currentList[index].updateModel(payload)
listDiffer.submitList(currentList)
}