最近在做一个应用的时候,需要为GridLayoutManager添加头部header,然后自然而然就想到了用不同的itemType去加载不同的布局。
1.实现多item布局,用不同的itemType去加载不同的布局。
主要思路就是先定义好标识itemType的常量,然后重写getItemViewType()方法,根据不同的位置(position)返回不同的Type,接着在onCreateViewHolder()中根据参数viewType去判断该item项应该 inflate 哪个布局文件,并返回相应的ViewHolder实例(这里ViewHolder是根据不同的item布局预先自定义好的不同的ViewHolder)
比如我的代码:
1 public class MyRecyclerCardviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ 2 3 public static enum ITEM_TYPE { 4 ITEM_TYPE_Theme, 5 ITEM_TYPE_Video 6 } 7 //数据集 8 public List<Integer> mdatas; 9 private TextView themeTitle; 10 11 public MyRecyclerCardviewAdapter(List<Integer> datas){ 12 super(); 13 this.mdatas = datas; 14 } 15 16 17 @Override 18 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 19 20 if (viewType == ITEM_TYPE.ITEM_TYPE_Theme.ordinal()){ 21 22 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.videothemelist,parent,false); 23 24 return new ThemeVideoHolder(view); 25 26 }else if(viewType == ITEM_TYPE.ITEM_TYPE_Video.ordinal()){ 27 28 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.videocardview,parent,false); 29 return new VideoViewHolder(view); 30 31 } 32 return null; 33 } 34 35 36 @Override 37 public void onBindViewHolder(final ViewHolder holder, final int position) { 38 39 if (holder instanceof ThemeVideoHolder){ 40 41 themeTitle.setText("励志"); 42 43 }else if (holder instanceof VideoViewHolder){ 44 ((VideoViewHolder)holder).videologo.setImageResource(R.drawable.lianzai_02); 45 ((VideoViewHolder)holder).videovname.setText("励志,俄小伙练习街头健身一年的体型变化,Dear Hard Work!"); 46 ((VideoViewHolder)holder).videoviewed.setText("2780次"); 47 ((VideoViewHolder)holder).videocomment.setText("209条"); 48 49 } 50 51 } 52 53 54 public int getItemViewType(int position){ 55 56 return position % 5 == 0 ? ITEM_TYPE.ITEM_TYPE_Theme.ordinal() : ITEM_TYPE.ITEM_TYPE_Video.ordinal(); 57 } 58 59 60 61 62 @Override 63 public int getItemCount() { 64 return mdatas.size(); 65 } 66 67 68 public class ThemeVideoHolder extends RecyclerView.ViewHolder{ 69 70 public ThemeVideoHolder(View itemView) { 71 super(itemView); 72 themeTitle = (TextView) itemView.findViewById(R.id.hometab1_theme_title); 73 } 74 } 75 76 public class VideoViewHolder extends RecyclerView.ViewHolder { 77 public ImageView videologo; 78 public TextView videovname; 79 public TextView videoviewed; 80 public TextView videocomment; 81 82 public VideoViewHolder(View itemView) { 83 super(itemView); 84 videologo = (ImageView) itemView.findViewById(R.id.videologo); 85 videoviewed = (TextView) itemView.findViewById(R.id.videoviewed); 86 videocomment = (TextView) itemView.findViewById(R.id.videocomment); 87 videovname = (TextView) itemView.findViewById(R.id.videoname); 88 } 89 } 90 }
这时,使用的是 LayoutManager 中发 LinearLayoutManager,效果图如下:
但是,当我们把 LayoutManager 改成GridLayoutManager的时候你就出现了不是我们期待的效果,如下图:
What the hell is going on? 什么鬼?怎么添加的header随着其他item项以cell的形式出现在网格上。仔细想一想,发现了下面代码
1 GridLayoutManager layoutManager = new GridLayoutManager(this,2, GridLayoutManager.VERTICAL,false);
哦!原来我们在创建GridLayoutManager的时候需要设定每行显示多少个item项,我们这里设置的是2,而我们添加的header是以item项的形式添加进来的,所以也会以cell的形式出现。那么,有没有办法让header这个item占据两个cell,单独霸占一行呢?答案是肯定的,我们可以通过setSpanSizeLookup抽象类中的getSpanSize()方法的返回值来设定每个item项占据多少个单元格 。
1 gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 2 @Override 3 public int getSpanSize(int position) { 4 return getItemViewType(position) == ITEM_TYPE.ITEM_TYPE_Theme.ordinal() 5 ? gridManager.getSpanCount() : 1; 6 } 7 });
那么,这段代码在自定义Adapter中应该添加在何处呢?放在onAttachedToRecyclerView()中再合适不过了。
1 public void onAttachedToRecyclerView(RecyclerView recyclerView) { 2 super.onAttachedToRecyclerView(recyclerView); 3 RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); 4 if(manager instanceof GridLayoutManager) { 5 final GridLayoutManager gridManager = ((GridLayoutManager) manager); 6 gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { 7 @Override 8 public int getSpanSize(int position) { 9 return getItemViewType(position) == ITEM_TYPE.ITEM_TYPE_Theme.ordinal() 10 ? gridManager.getSpanCount() : 1; 11 } 12 }); 13 } 14 }
这时就可以实现我想要的效果了,运行效果图如下:
代码我已经抽离出来放到我的Github了:RecycleViewWithHeader
2.最后说一下为什么为什么用RecyclerView取代ListView。
用过ListView的都知道,在ListView中若要复用视图缓存,就要在getView()方法中手动判断convertView是否为空,若不为空则复用视图缓存,若为空则重新加载视图,而RecyclerView相当于对ListView的Adapter进行了再次封装,把ListView手动判断是否有缓存的代码封装到RecyclerView内部,使这部分逻辑不可见,我们只需要通过getItemCount()方法告诉RecyclerView有多少项数据,然后在onCreateViewHolder()中加载item布局实例化ViewHolder,然后在onBindViewHolder()中完成数据的绑定即可。