Bootstrap

为RecyclerView的不同item项实现不同的布局(添加分类Header)

最近在做一个应用的时候,需要为GridLayoutManager添加头部header,然后自然而然就想到了用不同的itemType去加载不同的布局。


1.实现多item布局,用不同的itemType去加载不同的布局。

    主要思路就是先定义好标识itemType的常量,然后重写getItemViewType()方法,根据不同的位置(position)返回不同的Type,接着在onCreateViewHolder()中根据参数viewType去判断该item项应该 inflate 哪个布局文件,并返回相应的ViewHolder实例(这里ViewHolder是根据不同的item布局预先自定义好的不同的ViewHolder)


比如我的代码:

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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的形式出现在网格上。仔细想一想,发现了下面代码

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. GridLayoutManager layoutManager = new GridLayoutManager(this,2, GridLayoutManager.VERTICAL,false);  


哦!原来我们在创建GridLayoutManager的时候需要设定每行显示多少个item项,我们这里设置的是2,而我们添加的header是以item项的形式添加进来的,所以也会以cell的形式出现。那么,有没有办法让header这个item占据两个cell,单独霸占一行呢?答案是肯定的,我们可以通过setSpanSizeLookup抽象类中的getSpanSize()方法的返回值来设定每个item项占据多少个单元格 。


[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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()中再合适不过了。

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  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()中完成数据的绑定即可。

;