具有过滤和有效负载的RecyclerView适配器

问题描述 投票:0回答:1

问题

您好!有没有人实现了

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]
}
android filter android-recyclerview payload android-asynclistdiffer
1个回答
0
投票

是的,确实需要更新初始列表。我改变了什么:

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)
}
© www.soinside.com 2019 - 2024. All rights reserved.