ConcatAdapter 导致 getItemViewType() 到 return 错误值?
ConcatAdapter causing getItemViewType() to return the wrong value?
我有一个 RecyclerView 适配器,可以处理我使用多年的几种视图类型。最近,我发现了 ConcatAdapter
,但我似乎 运行 遇到了一个问题,它似乎为 getItemViewType()
.
返回了错误的值
我有一个用例,我想在其中显示 header 并在其下方显示项目列表。我为 header 创建了一个新的适配器 class,但列表项将继续使用我的旧适配器(它还处理其他几种视图类型)。
所以我连接了两个适配器:
HeaderAdapter headerAdapter = new HeaderAdapter(context);
PostAdapter postAdapter = new PostAdapter(context, postData);
ConcatAdapter concatAdapter = new ConcatAdapter(headerAdapter, postAdapter);
recyclerView.setAdapter(concatAdapter);
这工作正常,但是当我打开 activity 时,它立即崩溃并给出错误:
java.lang.ClassCastException: com.myapp.android.adapter.PostAdapter$PostViewHolder cannot be cast to com.myapp.android.adapter.PostAdapter$FooterViewHolder
这毫无意义,因为我什至从未在回收站视图中添加页脚。
我在 PostAdapter
中的 getItemViewType()
方法看起来像这样:
@Override
public int getItemViewType(int position) {
if (data.get(position) instanceof Footer) {
return TYPE_FOOTER;
} else if (data.get(position) instanceof Post) {
return TYPE_POST;
}
return -1;
}
我的 onBindViewHolder()
看起来像这样:
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case TYPE_FOOTER:
FooterViewHolder vh1 = (FooterViewHolder) holder;
configureFooterViewHolder(vh1, position);
break;
case TYPE_POST:
PostViewHolder vh2 = (PostViewHolder) holder;
configurePostViewHolder(vh2, position);
break;
default:
break;
}
}
但无论出于何种原因,当我 运行 调试器时,它位于 FooterViewHolder vh1 = (FooterViewHolder) holder;
行,即使我从未将页脚作为 PostAdapter
数据的一部分。
那么它为什么登陆这个?为什么 ConcatAdapter 会在处理多种视图类型的适配器中导致这种奇怪的行为?
当你绑定view holders时,你应该使用RecyclerView.Adapters.getItemViewType(position), not RecyclerView.ViewHolder.getItemViewType():
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) { // not holder.getItemViewType()
case TYPE_FOOTER:
FooterViewHolder vh1 = (FooterViewHolder) holder;
configureFooterViewHolder(vh1, position);
break;
case TYPE_POST:
PostViewHolder vh2 = (PostViewHolder) holder;
configurePostViewHolder(vh2, position);
break;
default:
break;
}
}
你得到了一个错误,因为 view holder 被回收了,它可以是任何 view holder,因此当你强制转换它到一个错误的 class 时它抛出 ClassCastException
。
好像是故意的。经过一些调试,我发现这一行的 adapter.getItemViewType(localPosition)
是正确的,但 ConcatAdapter 还做了一个额外的“从本地到全局项目视图类型的转换”,我从未配置过,但至少它覆盖了我本来想做的。
所以您根本看不到本地项目视图类型值,您只是从 ConcatAdapter 中获得一些随机分配的索引。由于所有这些都是私有的,您唯一的选择就是反射。
祝你好运。
(好的,对于真正的答案,您还可以启用 ConcatAdapter.Config(isolateViewTypes = false)
,确保所有项目视图类型彼此不同,它们实际上是不同的,而不仅仅是 returning 0
或 return super.getItemViewType(position)
左右,然后 getItemViewType
将不会创建 local -> global
映射以确保适配器之间的一致性,而是 return 本地项目视图为你打字)
val concatAdapter = ConcatAdapter(
ConcatAdapter.Config.Builder()
.setIsolateViewTypes(false)
.build(),
我有一个 RecyclerView 适配器,可以处理我使用多年的几种视图类型。最近,我发现了 ConcatAdapter
,但我似乎 运行 遇到了一个问题,它似乎为 getItemViewType()
.
我有一个用例,我想在其中显示 header 并在其下方显示项目列表。我为 header 创建了一个新的适配器 class,但列表项将继续使用我的旧适配器(它还处理其他几种视图类型)。
所以我连接了两个适配器:
HeaderAdapter headerAdapter = new HeaderAdapter(context);
PostAdapter postAdapter = new PostAdapter(context, postData);
ConcatAdapter concatAdapter = new ConcatAdapter(headerAdapter, postAdapter);
recyclerView.setAdapter(concatAdapter);
这工作正常,但是当我打开 activity 时,它立即崩溃并给出错误:
java.lang.ClassCastException: com.myapp.android.adapter.PostAdapter$PostViewHolder cannot be cast to com.myapp.android.adapter.PostAdapter$FooterViewHolder
这毫无意义,因为我什至从未在回收站视图中添加页脚。
我在 PostAdapter
中的 getItemViewType()
方法看起来像这样:
@Override
public int getItemViewType(int position) {
if (data.get(position) instanceof Footer) {
return TYPE_FOOTER;
} else if (data.get(position) instanceof Post) {
return TYPE_POST;
}
return -1;
}
我的 onBindViewHolder()
看起来像这样:
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case TYPE_FOOTER:
FooterViewHolder vh1 = (FooterViewHolder) holder;
configureFooterViewHolder(vh1, position);
break;
case TYPE_POST:
PostViewHolder vh2 = (PostViewHolder) holder;
configurePostViewHolder(vh2, position);
break;
default:
break;
}
}
但无论出于何种原因,当我 运行 调试器时,它位于 FooterViewHolder vh1 = (FooterViewHolder) holder;
行,即使我从未将页脚作为 PostAdapter
数据的一部分。
那么它为什么登陆这个?为什么 ConcatAdapter 会在处理多种视图类型的适配器中导致这种奇怪的行为?
当你绑定view holders时,你应该使用RecyclerView.Adapters.getItemViewType(position), not RecyclerView.ViewHolder.getItemViewType():
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) { // not holder.getItemViewType()
case TYPE_FOOTER:
FooterViewHolder vh1 = (FooterViewHolder) holder;
configureFooterViewHolder(vh1, position);
break;
case TYPE_POST:
PostViewHolder vh2 = (PostViewHolder) holder;
configurePostViewHolder(vh2, position);
break;
default:
break;
}
}
你得到了一个错误,因为 view holder 被回收了,它可以是任何 view holder,因此当你强制转换它到一个错误的 class 时它抛出 ClassCastException
。
好像是故意的。经过一些调试,我发现这一行的 adapter.getItemViewType(localPosition)
是正确的,但 ConcatAdapter 还做了一个额外的“从本地到全局项目视图类型的转换”,我从未配置过,但至少它覆盖了我本来想做的。
所以您根本看不到本地项目视图类型值,您只是从 ConcatAdapter 中获得一些随机分配的索引。由于所有这些都是私有的,您唯一的选择就是反射。
祝你好运。
(好的,对于真正的答案,您还可以启用 ConcatAdapter.Config(isolateViewTypes = false)
,确保所有项目视图类型彼此不同,它们实际上是不同的,而不仅仅是 returning 0
或 return super.getItemViewType(position)
左右,然后 getItemViewType
将不会创建 local -> global
映射以确保适配器之间的一致性,而是 return 本地项目视图为你打字)
val concatAdapter = ConcatAdapter(
ConcatAdapter.Config.Builder()
.setIsolateViewTypes(false)
.build(),