理解RecyclerView(二)—不同類型條目item、頭尾佈局

前言: 世人總是恐懼失敗,但失敗了也大不從頭再來。

一、概述

  我們在上一篇文章RecyclerView(一)中對RecyclerView使用詳細介紹了,在項目中,我們常遇到一個列表中有不同類型的item,RecyclerView.Adapter中還有一個很重要的方法getItemViewType(),它的作用是獲取item的類型,在onCreateViewHolder()方法中攜帶過來的viewType是區分item不同類型的參數,核心方法就在這裏。原理就是根據viewType來創建不同的ViewHolder,從而生成不同類型的item。

  • getItemViewType(int position)   根據位置獲取itemView的視圖類型,默認返回0,position表示item的位置。可以根據不同的唯一標識來區分不同的ViewType。

二、不同類型條目item

2.1 添加ViewHolder

我們這裏就只演示兩種不同的類型,一種普通的item類型,另一種爲廣告位類型,那麼我們根據這兩種類型創建兩個ViewHolder,同時需要兩個Item的佈局View:(源碼地址在最後給出)
普通的item類型:item_normal.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout ......
    android:id="@+id/cl_root">
    <ImageView
        ......
        android:id="@+id/iv_head"
        android:src="@mipmap/ic_head"/>
    <TextView
        ......
        android:id="@+id/tv_name"
        android:text="球隊名稱:"/>
    <TextView
        ......
        android:id="@+id/tv_price"
        android:text="$:30000000"/>
    <TextView
        ......
        android:id="@+id/tv_des"
        android:text="球隊描述:灌籃高手灌籃高手灌籃高手"/>
</androidx.constraintlayout.widget.ConstraintLayout>

廣告位類型:item_section.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ......>
    <ImageView 
        ......
        android:id="@+id/iv_bg"
        android:src="@mipmap/flower" />
</LinearLayout>

在adapter中分別創建普通類型和廣告位類型的ViewHolder

//普通類型ViewHolder
public class NormalHolder extends RecyclerView.ViewHolder {
    ImageView mIv_head;
    TextView mTv_name;
   
    NormalHolder(@NonNull View itemView) {
        super(itemView);
        mIv_head = itemView.findViewById(R.id.iv_head);
        mTv_name = itemView.findViewById(R.id.tv_name);
    }
}
//廣告位類型ViewHolder
public class SectionHolder extends RecyclerView.ViewHolder {
    ImageView mIv_bg;

    SectionHolder(@NonNull View itemView) {
        super(itemView);
        mIv_bg = itemView.findViewById(R.id.iv_bg);
    }
}

ViewHolder需要繼承RecyclerView.ViewHolder,然後構造方法傳入佈局View並需要實現super(itemView),將itemView通過父類方法傳給RecyclerView.ViewHolder,通過itemView將控件查找出來,以變量的形式保存到ViewHolder中,這樣就可以通過holder實例使用到查找出來的控件。

2.2綁定ViewHolder

首先我們在getItemViewType()中根據item的位置返回不同的類型,這裏模擬在position能被4整除的數爲特殊類型,其他爲普通類型:

    private static final int ITEM_TYPE_NORMAL= 0;//普通類型
    private static final int ITEM_TYPE_SECTION = 1;//特殊類型

	//返回每個數據中的itemType
    @Override
    public int getItemViewType(int position) {
        Goods goods = mData.get(position);
        return goods.getViewType();
    }

定義了兩個常量:ITEM_TYPE_NORMAL表示普通類型,ITEM_TYPE_SECTION 表示廣告位類型,常量值可以自己設置,能區分不同類型就可以了,最好規範起來,能被4整除的position就顯示特殊類型,其他則顯示普通類型。

然後在創建ViewHolder的方法onCreateViewHolder(ViewGroup parent, int viewType)中根據viewType不同類型創建不同類型的ViewHolder:

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        //根據不同類型來獲取不同的ViewHolder,裏面裝載不同的佈局
        if (viewType == ITEM_TYPE_SECTION) {
            return new SectionHolder(inflater.inflate(R.layout.item_section, parent, false));
        } else {
            return new NormalHolder(inflater.inflate(R.layout.item_normal, parent, false));
        }
    }

如果是普通類型就創建NormalHolder,特殊類型就創建SectionHolder。最後在綁定ViewHolder方法onBindViewHolder()將數據與ViewHolder綁定起來:

 	@Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
        if (holder instanceof NormalHolder) {//普通類型ViewHolder
            NormalHolder viewHolder = (NormalHolder) holder;
            viewHolder.mTv_name.setText(mData.get(position));
            viewHolder.mTv_price.setText("$:" + position * 100);
        } else if (holder instanceof SectionHolder) {//特殊類型ViewHolder
            SectionHolder sectionHolder = (SectionHolder) holder;
            sectionHolder.mIv_bg.setImageResource(R.mipmap.flower);
        }
    }

這裏可以holder instanceof NormalHolder來判斷ViewHolder的類型來處理相關的邏輯數據。那麼這裏顯示不同類型item的例子就完成了,效果如下:

核心原理就是根據viewType來創建不同的ViewHolder,從而生成不同類型的item,這裏給出adapter的全部代碼:TypeViewAdapter.java

public class TypeViewAdapter extends RecyclerView.Adapter {
    private Context mContext;
    private List<String> mData;
    private static final int ITEM_TYPE_NORMAL = 0;//普通類型
    private static final int ITEM_TYPE_SECTION = 1;//特殊類型

    public TypeViewAdapter(Context context, List<String> stringList) {
        this.mContext = context;
        this.mData = stringList;
    }

    @Override
    public int getItemViewType(int position) {
        Goods goods = mData.get(position);
        return goods.getViewType();
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        //根據不同類型來獲取不同的ViewHolder,裏面裝載不同的佈局
        if (viewType == ITEM_TYPE_SECTION) {
            return new SectionHolder(inflater.inflate(R.layout.item_section, parent, false));
        } else {
            return new NormalHolder(inflater.inflate(R.layout.item_normal, parent, false));
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
        if (holder instanceof NormalHolder) {//普通類型ViewHolder
            NormalHolder viewHolder = (NormalHolder) holder;
            viewHolder.mTv_name.setText(mData.get(position).getName());
            viewHolder.mTv_price.setText("$:" + position * 100);
            viewHolder.mCl_root.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext, "NormalHolder == " + position, Toast.LENGTH_SHORT).show();
                }
            });
        } else if (holder instanceof SectionHolder) {//特殊類型ViewHolder
            SectionHolder sectionHolder = (SectionHolder) holder;
            sectionHolder.mIv_bg.setImageResource(R.mipmap.flower);
            sectionHolder.mIv_bg.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(mContext, "SectionHolder == " + position, Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

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

    //普通類型ViewHolder
    public class NormalHolder extends RecyclerView.ViewHolder {
        ConstraintLayout mCl_root;
        TextView mTv_name;
        TextView mTv_price;

        NormalHolder(@NonNull View itemView) {
            super(itemView);
            mCl_root = itemView.findViewById(R.id.cl_root);
            mTv_name = itemView.findViewById(R.id.tv_name);
            mTv_price = itemView.findViewById(R.id.tv_price);
        }
    }

    //特殊類型ViewHolder
    public class SectionHolder extends RecyclerView.ViewHolder {
        ImageView mIv_bg;

        SectionHolder(@NonNull View itemView) {
            super(itemView);
            mIv_bg = itemView.findViewById(R.id.iv_bg);
        }
    }
}

TypeViewActivity.java

public class TypeViewActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview);
       
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        LinearLayoutManager manager = new LinearLayoutManager(this);
        manager.setOrientation(RecyclerView.VERTICAL);
        recyclerView.setLayoutManager(manager);

        List<Goods> list = new ArrayList<>();
        for (int i = 0; i < 40; i++) {
            Goods goods = new Goods();
            goods.setName("球隊名稱: " + i);
            //每第四個數據爲廣告類型,其他爲普通類型
            goods.setViewType(i % 4 == 0 ? TypeViewAdapter.ITEM_TYPE_SECTION : TypeViewAdapter.ITEM_TYPE_NORMAL);
            list.add(goods);
        }
        
        TypeViewAdapter adapter = new TypeViewAdapter(this, list);
        recyclerView.setAdapter(adapter);
    }
}

三、添加頭尾佈局

  在上面根據類型創建不同類型的item,主要原理是根據viewType來創建不同的ViewHolder,從而生成不同類型的item。RecyclerView默認沒有像ListView那樣提供類似addHeaderView()addFooterView()添加頭尾佈局的API,那麼我們也可以根據這個原理來爲RecyclerView添加頭佈局和尾佈局。(源碼在文章最後給出)

(1) 首先創建一個adapter,並繼承RecyclerView.Adapter,並重寫相關方法,首先設置三個item_type,分別是頭佈局,尾佈局,普通類型,在getItemViewType()方法中根據position判斷第一個數據返回頭佈局類型,最後一個數據返回尾佈局類型,其他item返回普通類型:

private static final int ITEM_TYPE_NORMAL = 0;//普通類型
private static final int ITEM_TYPE_HEAD = 1;//頭佈局類型
private static final int ITEM_TYPE_FOOT = 2;//尾部局類型

@Override
public int getItemViewType(int position) {
    if (mHeadView != null && position == 0) {//頭佈局
        return ITEM_TYPE_HEAD;
    } else if (mFootView != null && position == mData.size() - 1) {//尾部局
        return ITEM_TYPE_FOOT;
    }
    return ITEM_TYPE_NORMAL;//普通類型
}

(2)然後在onCreateViewHolder()方法中根據獲取的itemType創建不同類型的 ViewHolder:

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        //根據不同類型來獲取不同的ViewHolder,裏面裝載不同的佈局
        if (viewType == ITEM_TYPE_HEAD) {//頭佈局
            return new RecyclerView.ViewHolder(mHeadView) {};
        } else if (viewType == ITEM_TYPE_FOOT) {//尾部局
            return new RecyclerView.ViewHolder(mFootView) {};
        } else {
            return new NormalHolder(inflater.inflate(R.layout.item_linear, parent, false));
        }
    }

(3) 最後,在onBindViewHolder()實現控件與數據邏輯的綁定,adapter還提供了set()方法提供頭佈局和尾部局實例:

	@Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
        if (holder instanceof NormalHolder) {//普通類型ViewHolder
            NormalHolder viewHolder = (NormalHolder) holder;
            viewHolder.mTv_name.setText(mData.get(position));
        }
    }
    
   public void setHeadView(View headView) {//設置頭佈局
        mHeadView = headView;
    }
    public void setFootView(View footView) {//設置尾佈局
        mFootView = footView;
    }

效果如下:

下面給出代碼,在Activity中設置數據並添加頭部尾部:HeadFootViewActivity.java

public class HeadFootViewActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview);
        RecyclerView recyclerView = findViewById(R.id.recyclerView);

        //創建佈局管理器-線性佈局
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        //設置數據
        List<String> stringList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            stringList.add("第 " + i + " 個item");
        }

        //數據適配器
        HeadFootViewAdapter adapter = new HeadFootViewAdapter(this, stringList);

        //頭佈局
        View headView = LayoutInflater.from(this).inflate(R.layout.head_banner, recyclerView, false);
        //尾部局
        View footView = LayoutInflater.from(this).inflate(R.layout.foot_loadmore, recyclerView, false);
        
        adapter.setHeadView(headView);//添加頭佈局
        adapter.setFootView(footView);//添加尾佈局

        recyclerView.setAdapter(adapter);
    }
}

adapter的全部代碼:HeadFootViewAdapter.java

public class HeadFootViewAdapter extends RecyclerView.Adapter {
    private Context mContext;
    private List<String> mData;

    private View mHeadView;//頭佈局
    private View mFootView;//尾部局

    private static final int ITEM_TYPE_NORMAL = 0;//普通類型
    private static final int ITEM_TYPE_HEAD = 1;//頭佈局類型
    private static final int ITEM_TYPE_FOOT = 2;//尾部局類型

    public HeadFootViewAdapter(Context context, List<String> stringList) {
        this.mContext = context;
        this.mData = stringList;
    }

    @Override
    public int getItemViewType(int position) {
        if (mHeadView != null && position == 0) {//頭佈局
            return ITEM_TYPE_HEAD;
        } else if (mFootView != null && position == mData.size() - 1) {//尾部局
            return ITEM_TYPE_FOOT;
        }
        return ITEM_TYPE_NORMAL;//普通類型
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        //根據不同類型來獲取不同的ViewHolder,裏面裝載不同的佈局
        if (viewType == ITEM_TYPE_HEAD) {//頭佈局
            return new RecyclerView.ViewHolder(mHeadView) {};
        } else if (viewType == ITEM_TYPE_FOOT) {//尾部局
            return new RecyclerView.ViewHolder(mFootView) {};
        } else {
            return new NormalHolder(inflater.inflate(R.layout.item_linear, parent, false));
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
        if (holder instanceof NormalHolder) {//普通類型ViewHolder
            NormalHolder viewHolder = (NormalHolder) holder;
            viewHolder.mTv_name.setText(mData.get(position));
        }
    }

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

    //普通類型ViewHolder
    public class NormalHolder extends RecyclerView.ViewHolder {
        TextView mTv_name;

        NormalHolder(@NonNull View itemView) {
            super(itemView);
            mTv_name = itemView.findViewById(R.id.tv_name);
        }
    }

    public void setHeadView(View headView) {
        mHeadView = headView;
        if (mData == null) return;
        mData.add(0, "頭佈局");
    }

    public void setFootView(View footView) {
        mFootView = footView;
        if (mData == null) return;
        mData.add(mData.size(), "尾佈局");
    }
}

頭尾佈局的佈局文件就不給出了,比較簡單,如果需要可以在源碼中查看。

至此,本文結束!下篇將講解有關ItemTouchHelper拖拽和右滑刪除功能。


源碼地址:https://github.com/FollowExcellence/RecyclerViewDemo

請尊重原創者版權,轉載請標明出處:https://blog.csdn.net/m0_37796683/article/details/103711941 謝謝!


相關文章:

理解RecyclerView(一)

 ● RecyclerView的基礎使用、網格佈局、瀑布流佈局

理解RecyclerView(二)

 ● RecyclerView的ItemType(不同條目類型)

理解RecyclerView(三)

 ● RecyclerView的ItemDecoration分割線、增刪item動畫效果、拖拽和側滑刪除功能

理解RecyclerView(四)

 ● RecyclerView的自定義點擊事件、萬能ViewHolder和Adapter簡單封裝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章