為RecyclerView的不同item項實現不同的佈局(新增分類Header)
最近在做一個應用的時候,需要為GridLayoutManager新增頭部header,然後自然而然就想到了用不同的itemType去載入不同的佈局。
1.實現多item佈局,用不同的itemType去載入不同的佈局。
主要思路就是先定義好標識itemType的常量,然後重寫getItemViewType()方法,根據不同的位置(position)返回不同的Type,接著在onCreateViewHolder()中根據引數viewType去判斷該item項應該 inflate 哪個佈局檔案,並返回相應的ViewHolder例項(這裡ViewHolder是根據不同的item佈局預先自定義好的不同的ViewHolder)
比如我的程式碼:
這時,使用的是 LayoutManager 中發 LinearLayoutManager,效果圖如下:public class MyRecyclerCardviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ public static enum ITEM_TYPE { ITEM_TYPE_Theme, ITEM_TYPE_Video } //資料集 public List<Integer> mdatas; private TextView themeTitle; public MyRecyclerCardviewAdapter(List<Integer> datas){ super(); this.mdatas = datas; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == ITEM_TYPE.ITEM_TYPE_Theme.ordinal()){ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.videothemelist,parent,false); return new ThemeVideoHolder(view); }else if(viewType == ITEM_TYPE.ITEM_TYPE_Video.ordinal()){ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.videocardview,parent,false); return new VideoViewHolder(view); } return null; } @Override public void onBindViewHolder(final ViewHolder holder, final int position) { if (holder instanceof ThemeVideoHolder){ themeTitle.setText("勵志"); }else if (holder instanceof VideoViewHolder){ ((VideoViewHolder)holder).videologo.setImageResource(R.drawable.lianzai_02); ((VideoViewHolder)holder).videovname.setText("勵志,俄小夥練習街頭健身一年的體型變化,Dear Hard Work!"); ((VideoViewHolder)holder).videoviewed.setText("2780次"); ((VideoViewHolder)holder).videocomment.setText("209條"); } } public int getItemViewType(int position){ return position % 5 == 0 ? ITEM_TYPE.ITEM_TYPE_Theme.ordinal() : ITEM_TYPE.ITEM_TYPE_Video.ordinal(); } @Override public int getItemCount() { return mdatas.size(); } public class ThemeVideoHolder extends RecyclerView.ViewHolder{ public ThemeVideoHolder(View itemView) { super(itemView); themeTitle = (TextView) itemView.findViewById(R.id.hometab1_theme_title); } } public class VideoViewHolder extends RecyclerView.ViewHolder { public ImageView videologo; public TextView videovname; public TextView videoviewed; public TextView videocomment; public VideoViewHolder(View itemView) { super(itemView); videologo = (ImageView) itemView.findViewById(R.id.videologo); videoviewed = (TextView) itemView.findViewById(R.id.videoviewed); videocomment = (TextView) itemView.findViewById(R.id.videocomment); videovname = (TextView) itemView.findViewById(R.id.videoname); } } }
但是,當我們把 LayoutManager 改成GridLayoutManager的時候你就出現了不是我們期待的效果,如下圖:
What the hell is going on? 什麼鬼?怎麼新增的header隨著其他item項以cell的形式出現在網格上。仔細想一想,發現了下面程式碼
哦!原來我們在建立GridLayoutManager的時候需要設定每行顯示多少個item項,我們這裡設定的是2,而我們新增的header是以item項的形式新增進來的,所以也會以cell的形式出現。那麼,有沒有辦法讓header這個item佔據兩個cell,單獨霸佔一行呢?答案是肯定的,我們可以通過setSpanSizeLookup抽象類中的getSpanSize()方法的返回值來設定每個item項佔據多少個單元格 。GridLayoutManager layoutManager = new GridLayoutManager(this,2, GridLayoutManager.VERTICAL,false);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return getItemViewType(position) == ITEM_TYPE.ITEM_TYPE_Theme.ordinal()
? gridManager.getSpanCount() : 1;
}
});
那麼,這段程式碼在自定義Adapter中應該新增在何處呢?放在onAttachedToRecyclerView()中再合適不過了。
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if(manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return getItemViewType(position) == ITEM_TYPE.ITEM_TYPE_Theme.ordinal()
? gridManager.getSpanCount() : 1;
}
});
}
}
這時就可以實現我想要的效果了,執行效果圖如下:
2.最後說一下為什麼為什麼用RecyclerView取代ListView。
用過ListView的都知道,在ListView中若要複用檢視快取,就要在getView()方法中手動判斷convertView是否為空,若不為空則複用檢視快取,若為空則重新載入檢視,而RecyclerView相當於對ListView的Adapter進行了再次封裝,把ListView手動判斷是否有快取的程式碼封裝到RecyclerView內部,使這部分邏輯不可見,我們只需要通過getItemCount()方法告訴RecyclerView有多少項資料,然後在onCreateViewHolder()中載入item佈局例項化ViewHolder,然後在onBindViewHolder()中完成資料的繫結即可。