2

我有一个 RecyclerView 适配器,它负责处理我多年来一直使用的几种视图类型。最近,我发现了ConcatAdapter,但我似乎遇到了一个问题,它似乎返回了错误的值getItemViewType()

我有一个用例,我想在其中显示标题并在其下方显示项目列表。我已经为标题创建了一个新的适配器类,但列表项将继续使用我的旧适配器(它还负责其他几种视图类型)。

所以我连接了两个适配器:

HeaderAdapter headerAdapter = new HeaderAdapter(context);
PostAdapter postAdapter = new PostAdapter(context, postData);

ConcatAdapter concatAdapter = new ConcatAdapter(headerAdapter, postAdapter);
recyclerView.setAdapter(concatAdapter);

这工作正常,但是当我打开活动时,它立即崩溃并给出错误:

java.lang.ClassCastException:com.myapp.android.adapter.PostAdapter$PostViewHolder 无法转换为 com.myapp.android.adapter.PostAdapter$FooterViewHolder

这没有任何意义,因为我什至从未在回收站视图中添加页脚。

我的getItemViewType()方法PostAdapter看起来像这样:

@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;,即使我从未将页脚作为PostAdapters 数据的一部分。

那么它为什么会落在这上面呢?为什么 ConcatAdapter 会在处理多种视图类型的适配器中导致这种奇怪的行为?

4

2 回答 2

3

当你绑定视图持有者时,你应该使用RecyclerView.Adapters.getItemViewType(position),而不是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;
    }
}

你得到一个错误,因为视图持有者被回收了,它可以是任何视图持有者,因此ClassCastException当你强制将它转换为错误的类时它会抛出。

于 2020-10-14T22:41:01.703 回答
0

似乎是故意的。经过一番调试,我发现adapter.getItemViewType(localPosition)这一行是正确的,但 ConcatAdapter 还做了一个我从未配置过的额外“从本地到全局项目视图类型的转换”,但至少它覆盖了我最初想要的做。

mViewTypeLookup.localToGlobal()

所以你根本看不到本地项目视图类型值,你只是从 ConcatAdapter 获得一些随机分配的索引。由于所有这些都是私有的,因此您唯一的选择是反射。

祝你好运。

(好吧,对于一个真正的答案,您还可以启用ConcatAdapter.Config(isolateViewTypes = false),确保所有项目视图类型彼此不同,它们实际上是不同的,而不是仅仅返回0左右return super.getItemViewType(position),然后getItemViewType不会创建local -> global映射以确保跨适配器,而是它会为您返回本地项目视图类型)

    val concatAdapter = ConcatAdapter(
        ConcatAdapter.Config.Builder()
            .setIsolateViewTypes(false)
            .build(),
于 2021-12-13T22:55:09.940 回答