ListAdapter currentList 和 itemCount 在筛选或提交列表后不返回更新

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

我正在使用

ListAdapter
和实现
AsyncDifferConfig.Builder
Filterable
为 RecyclerView 实现可过滤列表。当搜索没有结果匹配时,将显示一个 TextView。

 adapter.filter.filter(filterConstraint)


 // Searched asset may not match any of the available item
 if (adapter.itemCount <= 0 && adapter.currentList.isEmpty() && filterConstraint.isNotBlank())
     logTxtV.setText(R.string.no_data)
 else
     logTxtV.text = null

不幸的是,过滤器的更新没有立即传播到适配器的计数和列表上。 适配器计数和列表落后一步。

TextView 应该已经显示在此处

但只有更新回来后才会显示,并且此时列表不再为空

我不确定这是否是因为我使用的是

AsyncDifferConfig.Builder
而不是常规的
DiffCallback

ListAdapter class
abstract class FilterableListAdapter<T, VH : RecyclerView.ViewHolder>(
    diffCallback: DiffUtil.ItemCallback<T>
) : ListAdapter<T, VH>(AsyncDifferConfig.Builder(diffCallback).build()), Filterable {

    /**
     * True when the RecyclerView stop observing
     * */
    protected var isDetached: Boolean = false

    private var originalList: List<T> = currentList.toList()

    /**
     * Abstract method for implementing filter based on a given predicate
     * */
    abstract fun onFilter(list: List<T>, constraint: String): List<T>

    override fun getFilter(): Filter {
        return object : Filter() {
            override fun performFiltering(constraint: CharSequence?): FilterResults {
                return FilterResults().apply {
                    values = if (constraint.isNullOrEmpty())
                        originalList
                    else
                        onFilter(originalList, constraint.toString())
                }
            }

            @Suppress("UNCHECKED_CAST")
            override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
                submitList(results?.values as? List<T>, true)
            }
        }
    }

    override fun submitList(list: List<T>?) {
        submitList(list, false)
    }

    /**
     * This function is responsible for maintaining the
     * actual contents for the list for filtering
     * The submitList for parent class delegates false
     * so that a new contents can be set
     * While a filter pass true which make sure original list
     * is maintained
     *
     * @param filtered True if the list was updated using filter interface
     * */
    private fun submitList(list: List<T>?, filtered: Boolean) {
        if (!filtered)
            originalList = list ?: listOf()

        super.submitList(list)
    }

    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
        super.onDetachedFromRecyclerView(recyclerView)
        isDetached = true
    }

}

RecyclerView适配器

class AssetAdapter(private val glide: RequestManager, private val itemListener: ItemListener) :
    FilterableListAdapter<AssetDataDomain, AssetAdapter.ItemView>(DiffUtilAsset()) {

    inner class ItemView(itemView: AssetCardBinding) : RecyclerView.ViewHolder(itemView.root) {
        private val assetName = itemView.assetName
        private val assetPrice = itemView.assetPrice
        private val assetMarketCap = itemView.assetMarketCap
        private val assetPercentChange = itemView.assetPercentChange
        private val assetIcon = itemView.assetIcon
        private val assetShare = itemView.assetShare

        // Full update/binding
        fun bind(domain: AssetDataDomain) {

            with(itemView.context) {

                assetName.text = domain.symbol ?: domain.name

                bindNumericData(
                    domain.metricsDomain.marketDataDomain.priceUsd,
                    domain.metricsDomain.marketDomain.currentMarketcapUsd,
                    domain.metricsDomain.marketDataDomain.percentChangeUsdLast24Hours
                )

                if (!isDetached)
                    glide
                        .load(
                            getString(
                                R.string.icon_url,
                                AppConfigs.ICON_BASE_URL,
                                domain.id
                            )
                        )
                        .into(assetIcon)

                assetShare.setOnClickListener {
                    itemListener.onRequestScreenShot(
                        itemView,
                        getString(
                            R.string.asset_info,
                            domain.name,
                            assetPercentChange.text.toString(),
                            assetPrice.text.toString()
                        )
                    )
                }

                itemView.setOnClickListener {
                    itemListener.onItemSelected(domain)
                }

            }


        }

        // Partial update/binding
        fun bindNumericData(priceUsd: Double?, mCap: Double?, percent: Double?) {

            with(itemView.context) {
                assetPrice.text = getString(
                    R.string.us_dollars,
                    NumbersUtil.formatFractional(priceUsd)
                )
                assetMarketCap.text = getString(
                    R.string.mcap,
                    NumbersUtil.formatWithUnit(mCap)
                )
                assetPercentChange.text = getString(
                    R.string.percent,
                    NumbersUtil.formatFractional(percent)
                )

                AppUtil.displayPercentChange(assetPercentChange, percent)

                if (NumbersUtil.isNegative(percent))
                    assetPrice.setTextColor(Color.RED)
                else
                    assetPrice.setTextColor(Color.GREEN)
            }

        }

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemView =
        ItemView(
            AssetCardBinding.inflate(
                LayoutInflater.from(parent.context), parent, false
            )
        )

    override fun onBindViewHolder(holder: ItemView, position: Int) {
        onBindViewHolder(holder, holder.absoluteAdapterPosition, emptyList())
    }

    override fun onBindViewHolder(holder: ItemView, position: Int, payloads: List<Any>) {

        if (payloads.isEmpty() || payloads[0] !is Bundle)
            holder.bind(getItem(position)) // Full update/binding
        else {

            val bundle = payloads[0] as Bundle

            if (bundle.containsKey(DiffUtilAsset.ARG_PRICE) ||
                bundle.containsKey(DiffUtilAsset.ARG_MARKET_CAP) ||
                bundle.containsKey(DiffUtilAsset.ARG_PERCENTAGE))
                holder.bindNumericData(
                    bundle.getDouble(DiffUtilAsset.ARG_PRICE),
                    bundle.getDouble(DiffUtilAsset.ARG_MARKET_CAP),
                    bundle.getDouble(DiffUtilAsset.ARG_PERCENTAGE)
                ) // Partial update/binding

        }

    }

    // Required when setHasStableIds is set to true
    override fun getItemId(position: Int): Long {
        return currentList[position].id.hashCode().toLong()
    }

    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
        super.onDetachedFromRecyclerView(recyclerView)
        isDetached = true
    }

    override fun onFilter(list: List<AssetDataDomain>, constraint: String): List<AssetDataDomain> {

        return list.filter {
            it.name.lowercase().contains(constraint.lowercase()) ||
                    it.symbol?.lowercase()?.contains(constraint.lowercase()) == true
        }

    }

    interface ItemListener {

        fun onRequestScreenShot(view: View, description: String)
        fun onItemSelected(domain: AssetDataDomain)

    }

}

更新:

我可以确认使用

DiffCallback
代替
AsyncDifferConfig.Builder
不会改变行为和问题。似乎
currentList
处于异步状态,因此列表上的更新在调用
submitList
后不会立即反映。

我不知道这是否是预期的行为,但在覆盖

onCurrentListChanged
后,
currentList
参数就是我正在寻找的。

但是

adapter.currentList
的行为就像
previousList
参数

android android-recyclerview listadapter android-diffutils android-listadapter
1个回答
0
投票

当您向

recyclerView
提交列表时,需要一些时间来比较当前列表和前一个列表的项目(以查看项目是否被删除、移动或添加)。所以结果还没有立即准备好。

您可以使用

RecyclerView.AdapterDataObserver
来通知
recyclerView
中的更改(它将告诉项目总体发生了什么,例如添加了 5 个项目等)

附注如果你查看

recyclerView
源代码,你会发现构造函数中传递的
DiffCallBack
被包裹在
AsyncDifferConfig

© www.soinside.com 2019 - 2024. All rights reserved.