具有多个视图类型的RecyclerAdapter中的IndexOutOfBoundsException

时间:2016-07-04 12:53:00

标签: java android android-recyclerview indexoutofboundsexception recycler-adapter

尝试将视图类型作为数组中数据位置的函数返回时,我收到了IndexOutOfBoundsException。我有两个数据来源。一个是广告,另一个是名称和位置列表。在在RecyclerAdapter中处理数组之前,是否需要将数组合并为一个数组?我不愿意,特别是如果原则上它们可以是不同的类型,即图像与文本。我的getItemCount()方法正确返回两个数组的总和大小。为什么我得到这个例外?如何在两种不同的视图类型中实现从两个不同来源显示数据的预期结果?

我的主要活动:

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()));
    }

}

我的回收器适配器允许多个视图:

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());
        }
    }
}

3 个答案:

答案 0 :(得分:3)

您需要创建一个添加列表的公共列表。只需执行以下操作 -

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

现在更改get item count -

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

希望它对您有用:)

答案 1 :(得分:2)

getItemCount的实施错误

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

如果两者都没有返回0,则返回的值大于两个集合的大小。这就是为什么你得到一个ArrayIndexOutBoundException。请注意,position0发送到getItemCount - 1,在您的情况下,itemsads都不包含getItemCount个对象

答案 2 :(得分:1)

getItemViewType的实施是错误的。让我们假设您的items列表有4个项目,ads有3个广告对象。现在你的getItemViewType将返回7.现在系统将开始查找这7个对象的项类型。让我们假设它想在RecyclerView中找到第5行的类型。所以现在在你的方法getItemViewType中,行号1本身会抛出此异常,因为items列表的大小仅为4。

要么必须将这两个列表合并到单个列表中,要么在回收站视图项目中有一些顺序,就像所有广告将出现在项目之前一样。在这种情况下,您还必须修改getItemViewType

希望这会有所帮助。