1. 程式人生 > >為RecyclerView的不同item項實現不同的佈局(新增分類Header)

為RecyclerView的不同item項實現不同的佈局(新增分類Header)

最近在做一個應用的時候,需要為GridLayoutManager新增頭部header,然後自然而然就想到了用不同的itemType去載入不同的佈局。

1.實現多item佈局,用不同的itemType去載入不同的佈局。

    主要思路就是先定義好標識itemType的常量,然後重寫getItemViewType()方法,根據不同的位置(position)返回不同的Type,接著在onCreateViewHolder()中根據引數viewType去判斷該item項應該 inflate 哪個佈局檔案,並返回相應的ViewHolder例項(這裡ViewHolder是根據不同的item佈局預先自定義好的不同的ViewHolder)

比如我的程式碼:

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 中發 LinearLayoutManager,效果圖如下:

但是,當我們把 LayoutManager 改成GridLayoutManager的時候你就出現了不是我們期待的效果,如下圖:


What the hell is going on? 什麼鬼?怎麼新增的header隨著其他item項以cell的形式出現在網格上。仔細想一想,發現了下面程式碼

GridLayoutManager layoutManager = new GridLayoutManager(this,2, GridLayoutManager.VERTICAL,false);
哦!原來我們在建立GridLayoutManager的時候需要設定每行顯示多少個item項,我們這裡設定的是2,而我們新增的header是以item項的形式新增進來的,所以也會以cell的形式出現。那麼,有沒有辦法讓header這個item佔據兩個cell,單獨霸佔一行呢?答案是肯定的,我們可以通過setSpanSizeLookup抽象類中的getSpanSize()方法的返回值來設定每個item項佔據多少個單元格 。
    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()中完成資料的繫結即可。