bluemunch bluemunch - 5 months ago 40
Android Question

IndexOutOfBoundsException in RecyclerAdapter with Multiple View Types

I'm getting an IndexOutOfBoundsException when attempting to return the view type as a function of the position of the data in my array. I have two sources of data. One is an advertisement, and the other is a list of names and locations. Do I need to combine the arrays into one single array before processing it in the RecyclerAdapter? I'd rather not, especially if in principle they could be different types, i.e. images versus text. My

getItemCount()
method properly returns the summed size of the two arrays. Why am I getting this exception? How can I achieve the desired result of showing data from two different sources in two different view types?

My main activity:

public class MainActivity extends AppCompatActivity {

private ArrayList<Object> getPresidentsArrayList() {
ArrayList<Object> items = new ArrayList<>();
items.add(new President("George Washington", "Mount Vernon"));
items.add(new President("John Adams", "Braintree"));
items.add(new President("Thomas Jefferson", "Monticello"));
items.add(new President("James Madison", "Port Conway"));
return items;
}

private ArrayList<Object> getAdsArrayList() {
ArrayList<Object> ads = new ArrayList<>();
ads.add(new Sponsored("Craft Beer 20% Off", "Budweiser"));
ads.add(new Sponsored("Craft Beer 20% Off", "Budweiser"));
ads.add(new Sponsored("Craft Beer 20% Off", "Budweiser"));
ads.add(new Sponsored("Craft Beer 20% Off", "Budweiser"));
return ads;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
bindDataToAdapter(recyclerView);

}

private void bindDataToAdapter(RecyclerView recyclerView) {
recyclerView.setAdapter(new HeterogeneousRecyclerAdapter(getPresidentsArrayList(), getAdsArrayList()));
}

}


My recycler adapter allowing for multiple views:

public class HeterogeneousRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

private List<Object> items;
private List<Object> ads;
private final int PRESIDENT = 0, SPONSORED = 1;

public HeterogeneousRecyclerAdapter(List<Object> items, List<Object> ads) {
this.items = items;
this.ads = ads;
}

@Override
public int getItemCount() {
return (this.items.size() + this.ads.size());
}

@Override
public int getItemViewType(int position) {
if (items.get(position) instanceof President) {
return PRESIDENT;
} else if (ads.get(position) instanceof Sponsored) {
return SPONSORED;
}
return -1;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {

RecyclerView.ViewHolder viewHolder;
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());

switch (viewType) {
case PRESIDENT:
View v1 = inflater.inflate(R.layout.layout_viewholder1, viewGroup, false);
viewHolder = new ViewHolder1(v1);
break;
case SPONSORED:
View v2 = inflater.inflate(R.layout.layout_viewholder2, viewGroup, false);
viewHolder = new ViewHolder2(v2);
break;
default:
View v = inflater.inflate(android.R.layout.simple_list_item_1, viewGroup, false);
viewHolder = new RecyclerViewSimpleTextViewHolder(v);
break;
}
return viewHolder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
switch (viewHolder.getItemViewType()) {
case PRESIDENT:
ViewHolder1 vh1 = (ViewHolder1) viewHolder;
configureViewHolder1(vh1, position);
break;
case SPONSORED:
ViewHolder2 vh2 = (ViewHolder2) viewHolder;
configureViewHolder2(vh2, position);
break;
default:
RecyclerViewSimpleTextViewHolder vh = (RecyclerViewSimpleTextViewHolder) viewHolder;
configureDefaultViewHolder(vh, position);
break;
}
}

private void configureDefaultViewHolder(RecyclerViewSimpleTextViewHolder vh, int position) {
vh.getLabel().setText((CharSequence) items.get(position));
}

private void configureViewHolder1(ViewHolder1 vh1, int position) {
President president = (President) items.get(position);
if (president != null) {
vh1.getLabel1().setText(president.getName());
vh1.getLabel2().setText(president.getHometown());
}
}

private void configureViewHolder2(ViewHolder2 vh2, int position) {
Sponsored sponsored = (Sponsored) ads.get(position);
if (sponsored != null) {
vh2.getLabel1().setText(sponsored.getName());
vh2.getLabel2().setText(sponsored.getCompany());
}
}
}

Neo Neo
Answer

You need to create a common list adding both your list. Just do the following -

    public HeterogeneousRecyclerAdapter(List<Object> items, List<Object> ads) {
        this.items = items;
        items.addAll(ads);
    }

Now change the get Item count -

@Override
public int getItemCount() {
    return this.items.size();
}

Hope it will work fine for you :)

Comments