Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I have a RecyclerView adapter that takes care of several view types which I've been using for years. Recently, I found out about
ConcatAdapter
, but I seem to be running into an issue where it seems to be returning the wrong value for
getItemViewType()
.
I have a use case where I want to show a header and below it a list of items. I've created a new adapter class for the header, but the list items will continue using my old adapter (that also takes care of several other view types).
So I've concatenated the two adapters:
HeaderAdapter headerAdapter = new HeaderAdapter(context);
PostAdapter postAdapter = new PostAdapter(context, postData);
ConcatAdapter concatAdapter = new ConcatAdapter(headerAdapter, postAdapter);
recyclerView.setAdapter(concatAdapter);
This works fine, but when I go to open the activity, it immediately crashes and gives the error:
java.lang.ClassCastException: com.myapp.android.adapter.PostAdapter$PostViewHolder cannot be cast to com.myapp.android.adapter.PostAdapter$FooterViewHolder
Which makes no sense because I never even added a footer to the recycler view.
My getItemViewType()
method in PostAdapter
looks something like this:
@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;
And my onBindViewHolder()
looks something like this:
@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;
But for whatever reason, when I run the debugger, it lands on the line FooterViewHolder vh1 = (FooterViewHolder) holder;
, even though I never included a footer as part of the PostAdapter
s data.
So why is it landing on this? Why would ConcatAdapter cause such strange behaviour in an adapter that handles multiple view types?
When you bind the view holders, you should use 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;
You got an error because the view holder was recycled, it could be any view holder, therefore it threw ClassCastException
when you forced to cast it to a wrong class.
Seems to be intentional. After a bit of debugging, I find that the adapter.getItemViewType(localPosition)
on this line would be correct, but ConcatAdapter also does an additional "conversion from Local to Global item view types" that I've never configured, but at least it overrides what I originally wanted to do.
So you can't see the local item view type values at all, you just get some randomly assigned index from the ConcatAdapter. As all of it is private, your only option is reflection.
Good luck.
(okay, for a real answer, you can also enable ConcatAdapter.Config(isolateViewTypes = false)
, ensure that all the item view types are different from one another where they are actually different instead of just returning 0
or return super.getItemViewType(position)
or so, and then getItemViewType
will not create a local -> global
mapping to ensure consistency across the adapters, but rather it will return the local item view types for you)
val concatAdapter = ConcatAdapter(
ConcatAdapter.Config.Builder()
.setIsolateViewTypes(false)
.build(),
–
–
–
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.