前言: 世人總是恐懼失敗,但失敗了也大不從頭再來。
一、概述
我們在上一篇文章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簡單封裝