1

當試圖返回視圖類型作爲數據在我的數組中的位置的函數時,我得到一個IndexOutOfBoundsException。我有兩個數據來源。一個是廣告,另一個是名稱和位置的列表。在RecyclerAdapter中處理數組之前,是否需要將數組合併成一個數組?我寧願不,特別是如果原則上它們可以是不同的類型,即圖像與文本。我的getItemCount()方法正確地返回兩個數組的總和大小。爲什麼我得到這個異常?我如何在兩種不同視圖類型中實現顯示來自兩個不同源的數據的預期結果?具有多種視圖類型的RecyclerAdapter中的IndexOutOfBoundsException

我的主要活動:

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

您需要創建一個共同的列表中添加兩個列表。只要做到以下 -

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

現在改變獲取項目計數 -

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

希望它會正常工作爲你:)

+0

我在哪裏添加「廣告」到我的物品清單? – santafebound

+0

只在適配器構造函數的最後一行。現在不需要保留廣告的參考。 – Neo

+0

好吧,明白了''public HeterogeneousRecyclerAdapter(List items,List ads){ this.items = items; items.addAll(ads); }''然後在整個過程中引用''items''。 – santafebound

2

getItemCount的實現是錯誤的

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

如果兩者都不返回0,則返回的值大於兩個集合的大小。這就是爲什麼你會得到一個ArrayIndexOutBoundException。請記住,position去從0getItemCount - 1,在你的情況下,無論items也不ads包含getItemCount對象

1

getItemViewType的實現是錯誤的。讓我們假設你的items列表有4項,ads有3個廣告對象。現在您的getItemViewType將返回7.現在系統將開始查找這7個對象的項目類型。讓我們假設它想要在RecyclerView中找到第5行的類型。所以現在在你的方法getItemViewType行號1本身拋出這個異常,因爲items列表的大小隻有4。

要麼你將不得不將這兩個列表合併到單個列表中,或者在回收器視圖項目之間有一些順序,就像所有的廣告將出現在項目之前一樣。在這種情況下,你也必須修改你的getItemViewType

希望這會有所幫助。