我决定使用新的 RecyclerView.ListAdapter ,并尝试将其用于生产。
我的计划是在加载列表项时显示一个占位符(骨架)项,然后在客户端接收到数据后切换到普通项列表。我在 adapter 中处理此问题的方式是,将一个'null'传递给 submitList()(这样我就知道数据尚未加载)。
第一部分效果很好, ListAdapter 启动并显示框架项,但是一旦加载了“真实”列表,应用程序就会崩溃。崩溃的原因是 adapter 直接进入 onBindViewHolder()而不先调用 onCreateViewHolder(),因此该尝试绑定数据到骨架项目中不存在的普通项目的字段。
到目前为止,我还无法找出为什么它跳过了第一项的 onCreateViewHolder()部分并直接进入绑定的原因。我检查了 diffUtils 是否存在问题,但是两个项目(框架项目和“真实”项目)明显不同。 (这里的简短说明:由于某些原因,两个 diffUtils 函数中的断点根本不会触发..我不确定这是否也是问题的一部分,或者仅仅是 Android Studio 问题。)
重要的主要类别:
ListAdapter
class VIPSimilarAdsAdapter(context: Context, presenterInterface: BaseVIPContract.MVPAdapterPresenter) : ListAdapter<Ad, AdViewHolderFactory.AdBaseViewHolder>(SimilarAdsDiffCallback()) {
private val presenterInterface: WeakReference<BaseVIPContract.MVPAdapterPresenter> = WeakReference(presenterInterface)
private var showLoadingInidicator = true
override fun submitList(list: List<Ad>?) {
showLoadingInidicator = list == null
super.submitList(list)
}
override fun getItemCount(): Int {
return when (showLoadingInidicator) {
true -> 1
else -> super.getItemCount()
}
}
override fun onBindViewHolder(@NonNull holder: AdViewHolderFactory.AdBaseViewHolder, position: Int) {
if (!showLoadingInidicator) {
getItem(position)?.let { safeAd ->
CommonViewBinder.bindCommonFields(holder as AdViewHolderFactory.AdViewHolder, safeAd)
AdViewBinder.bindAdFields(holder, safeAd)
holder.itemView.setOnClickListener {
presenterInterface.get()?.onUserEventSimilarAdClicked(safeAd, position)
}
}
}
}
@NonNull
override fun onCreateViewHolder(@NonNull parent: ViewGroup, viewType: Int): AdViewHolderFactory.AdBaseViewHolder {
return (when (showLoadingInidicator) {
true -> AdViewHolderFactory.SkeletonViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.grid_item_skeleton, parent, false), ITEM_VIEW_TYPE_SKELETON)
else -> AdViewHolderFactory.AdViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.grid_item_ad_basic, parent, false))
})
}
}
DiffUtil回调
class SimilarAdsDiffCallback : DiffUtil.ItemCallback<Ad>() {
override fun areItemsTheSame(oldItem: Ad, newItem: Ad): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Ad, newItem: Ad): Boolean {
return oldItem.id == newItem.id
}
}
适配器的简称是
adAdapterSimilarAds.submitList(null)
在活动开始时使用
adAdapterSimilarAds.submitList(list)
一旦我们加载了数据。
希望任何人以前都遇到过这个问题,可以帮助我...
编辑: 这是崩溃的堆栈跟踪
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{4e5bcfd position=6 id=-1, oldPos=0, pLpos:0 scrap [attachedScrap] tmpDetached no parent} androidx.recyclerview.widget.RecyclerView{3d89e2f VFED..... ......ID 0,1953-1080,2289 #7f090302 app:id/similar_ads_after_pos_1}, adapter:ebk.vip.similar_ads.VIPSimilarAdsAdapter@ac7dc3c, layout:androidx.recyclerview.widget.GridLayoutManager@e9b3dc5, context:ebk.vip.vip_core.VIPActivity@ecfbcc3
at androidx.recyclerview.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5715)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5898)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5858)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5854)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2230)
at androidx.recyclerview.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:557)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1517)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:612)
at androidx.recyclerview.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:171)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3875)
at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3330)
at android.view.View.measure(View.java:19857)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6083)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:758)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:640)
at android.view.View.measure(View.java:19857)
at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
at android.view.View.measure(View.java:19857)
at androidx.core.widget.NestedScrollView.measureChildWithMargins(NestedScrollView.java:1502)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at androidx.core.widget.NestedScrollView.onMeasure(NestedScrollView.java:556)
at android.view.View.measure(View.java:19857)
at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
at android.view.View.measure(View.java:19857)
at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
at android.view.View.measure(View.java:19857)
at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
at android.view.View.measure(View.java:19857)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6083)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:143)
at android.view.View.measure(View.java:19857)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6083)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.view.View.measure(View.java:19857)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6083)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
at android.view.View.measure(View.java:19857)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6083)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:758)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:640)