前言: 世人总是恐惧失败,但失败了也大不从头再来。
一、概述
我们在上一篇文章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(), "尾布局");
}
}
头尾布局的布局文件就不给出了,比较简单,如果需要可以在源码中查看。
点关注,不迷路
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。
我是suming,感谢各位的支持和认可,您的点赞、评论、收藏【一键三连】就是我创作的最大动力,我们下篇文章见!
如果本篇博客有任何错误,请批评指教,不胜感激 !
要想成为一个优秀的安卓开发者,这里有必须要掌握的知识架构,一步一步朝着自己的梦想前进!Keep Moving!
相关文章:
理解RecyclerView(一)
● RecyclerView的基础使用、网格布局、瀑布流布局
理解RecyclerView(二)
● RecyclerView的ItemType(不同条目类型)
理解RecyclerView(三)
● RecyclerView的ItemDecoration分割线、增删item动画效果、拖拽和侧滑删除功能
理解RecyclerView(四)
● RecyclerView的自定义点击事件、万能ViewHolder和Adapter简单封装