前言
RecyclerView是一个十分牛逼的控件,在列表显示的时候,几乎离不开它。由于列表显示的多样性,对RecyclerView对应的RecyclerView.Adapter要求也更多了些。那么今天我将引领大家以一种不同的角度来实现RecyclerView的多布局列表显示。
今天涉及的内容:
- RecyclerView多type显示基本思路
- RecyclerView多布局显示新思路
- DefaultAdapter2实现列表展示
- MainActivity中调用范例
- 效果图及项目结构图
- DefaultAdapter2源码
先来波效果图
相同布局效果图
不同布局效果图
空布局效果图
一.RecyclerView多type显示基本思路
一般我们在设置不同布局的列表展示的时候,大致做法如下:
public class HomePageAdapter extends RecyclerView.Adapter {
public static final int TYPE_BANNER = 0;
public static final int TYPE_AD = 1;
public static final int TYPE_TEXT = 2;
public static final int TYPE_IMAGE = 3;
public static final int TYPE_NEW = 4;
private List<HomePageEntry> mData;
public void setData(List<HomePageEntry> data) {
mData = data;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType){
case TYPE_BANNER:
return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_banner_layout,null));
case TYPE_AD:
return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_ad_item_layout,null));
case TYPE_TEXT:
return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_text_item_layout,null));
case TYPE_IMAGE:
return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_image_item_layout,null));
case TYPE_NEW:
return new BannerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.home_news_item_layout,null));
}
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int type = getItemViewType(position);
switch (type){
case TYPE_BANNER:
// banner 逻辑处理
break;
case TYPE_AD:
// 广告逻辑处理
break;
case TYPE_TEXT:
// 文本逻辑处理
break;
case TYPE_IMAGE:
//图片逻辑处理
break;
case TYPE_NEW:
//视频逻辑处理
break;
// ... 此处省去N行代码
}
}
@Override
public int getItemViewType(int position) {
if(position == 0){
return TYPE_BANNER;//banner在开头
}else {
return mData.get(position).type;//type 的值为TYPE_AD,TYPE_IMAGE,TYPE_AD,等其中一个
}
}
@Override
public int getItemCount() {
return mData == null ? 0:mData.size();
}
public static class BannerViewHolder extends RecyclerView.ViewHolder{
public BannerViewHolder(View itemView) {
super(itemView);
//绑定控件
}
}
public static class NewViewHolder extends RecyclerView.ViewHolder{
public VideoViewHolder(View itemView) {
super(itemView);
//绑定控件
}
}
public static class AdViewHolder extends RecyclerView.ViewHolder{
public AdViewHolder(View itemView) {
super(itemView);
//绑定控件
}
}
public static class TextViewHolder extends RecyclerView.ViewHolder{
public TextViewHolder(View itemView) {
super(itemView);
//绑定控件
}
}
public static class ImageViewHolder extends RecyclerView.ViewHolder{
public ImageViewHolder(View itemView) {
super(itemView);
//绑定控件
}
}
}
这样做会导致Adapter类代码量过大,不利于维护。
二.RecyclerView多布局显示新思路
基于多布局的情况,思路是将每个要设置的数据都从adapter中抽离成一个单独的data,然后在单独的data中去设置控件初始化,数据展示和点击事件等,然后让总的adapter不会因为布局添加的种类和个数而做修改。
据此,我写了一个 DefaultAdapter2 类,继承自 RecyclerView.Adapter,重写 DefaultAdapter2 中的 onCreateViewHolder,onBindViewHolder,getItemViewType 方法,将布局设置,数据设置和控件等初始化均封装到 接口 InterItemData 中,所有需要展示的数据均封装到 InterItemData 中,然后在 adapter 中的数据以 InterItemData 形式展示出来。
三.DefaultAdapter2实现列表展示
3.1 DefaultAdapter2初始化
//声明
private DefaultAdapter2 mNameAdapter;
private List<DefaultAdapter2.InterItemData>mItemDatas;
//初始化
mItemDatas=new ArrayList<>();
mNameAdapter=new DefaultAdapter2(mItemDatas,MainActivity.this);
mNameAdapter.setRecyclerManager(mRecyclerView);
3.2 DefaultAdapter2设置相同布局的数据
List<String>names=new ArrayList<>();
for (int i = 0; i < 10; i++) {
names.add("aa"+i);
}
//将names中的数据用DefaultAdapter2.InterItemData封装然后展示
for (int i = 0; i < names.size(); i++) {
String name=names.get(i);
DefaultAdapter2.InterItemData itemData=new DefaultAdapter2.InterItemData() {
@Override
public int getLayoutId() {
return R.layout.item_layout;
}
@Override
public void onBindView(DefaultAdapter2.ViewHolder viewHolder, int position) {
//初始化
TextView tv = (TextView) viewHolder.getView(R.id.tv);
//设置数据
String name = mItemDatas.get(position).getData().toString();
tv.setText(name);
//点击事件
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.longShow(name);
}
});
}
@Override
public <T> T getData() {
//设置数据源
return (T) name;
}
};
//添加数据
mItemDatas.add(itemData);
}
3.3 DefaultAdapter2设置不同布局的数据
//布局一数据
DefaultAdapter2.InterItemData itemData1=new DefaultAdapter2.InterItemData() {
@Override
public int getLayoutId() {
return R.layout.item_layout;
}
@Override
public void onBindView(DefaultAdapter2.ViewHolder viewHolder, int position) {
//初始化
TextView tv = (TextView) viewHolder.getView(R.id.tv);
//设置数据
String name = mItemDatas.get(position).getData().toString();
tv.setText(name);
//点击事件
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.longShow(name + " " + position);
}
});
}
@Override
public <T> T getData() {
//设置数据源
return (T)"我是布局一";
}
};
//布局二数据
DefaultAdapter2.InterItemData itemData2=new DefaultAdapter2.InterItemData() {
@Override
public int getLayoutId() {
return R.layout.empty_layout;
}
@Override
public void onBindView(DefaultAdapter2.ViewHolder viewHolder, int position) {
//初始化
Button btn= (Button) viewHolder.getView(R.id.btn_reload);
//设置数据
String name = mItemDatas.get(position).getData().toString();
btn.setText(name);
//点击事件
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.longShow(name+"中的按钮");
}
});
//布局尺寸
ViewGroup.LayoutParams params= viewHolder.getItemHolderView().getLayoutParams();
params.height= ViewGroup.LayoutParams.WRAP_CONTENT;
viewHolder.getItemHolderView().setLayoutParams(params);
//布局点击
viewHolder.getItemHolderView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.longShow(name + " " + position);
}
});
}
@Override
public <T> T getData() {
//设置数据源
return (T)"我是布局二";
}
};
//添加数据
mItemDatas.add(itemData1);
mItemDatas.add(itemData2);
3.4 DefaultAdapter2设置空布局
//加载空布局
DefaultAdapter2.InterItemData itemDataEmpty=new DefaultAdapter2.InterItemData() {
@Override
public int getLayoutId() {
return R.layout.empty_layout;
}
@Override
public void onBindView(DefaultAdapter2.ViewHolder viewHolder, int position) {
//初始化
Button btn= (Button) viewHolder.getView(R.id.btn_reload);
//设置数据
String name = mItemDatas.get(position).getData().toString();
btn.setText(name);
//点击事件
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.longShow(name + " " + position);
}
});
}
@Override
public <T> T getData() {
//设置数据源
return (T)"重新加载";
}
};
//添加数据
mItemDatas.add(itemDataEmpty);
四.MainActivity中调用范例
/**
* Title:
*
* description:
* autor:pei
* created on ${DATE}
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button mBtn1;
private Button mBtn2;
private Button mBtn3;
private RecyclerView mRecyclerView;
private DefaultAdapter2 mNameAdapter;
private List<DefaultAdapter2.InterItemData>mItemDatas;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
setListener();
}
private void initData() {
mBtn1 = findViewById(R.id.btn1);
mBtn2 = findViewById(R.id.btn2);
mBtn3 = findViewById(R.id.btn3);
mRecyclerView = findViewById(R.id.rv);
mBtn1.setText("相同布局");
mBtn2.setText("不同布局");
mBtn3.setText("空布局");
mItemDatas=new ArrayList<>();
mNameAdapter=new DefaultAdapter2(mItemDatas,MainActivity.this);
mNameAdapter.setRecyclerManager(mRecyclerView);
}
private void setListener() {
mBtn1.setOnClickListener(this);
mBtn2.setOnClickListener(this);
mBtn3.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1://加载多条数据
//清理
mItemDatas.clear();
//加载多条数据
loadMoreData();
mNameAdapter.notifyDataSetChanged();
break;
case R.id.btn2://加载不同布局
//清理
mItemDatas.clear();
//加载不同布局
loadDiffLayoutData();
mNameAdapter.notifyDataSetChanged();
break;
case R.id.btn3://加载空布局
//清理
mItemDatas.clear();
//加载空布局
loadEmptyLayoutData();
mNameAdapter.notifyDataSetChanged();
break;
default:
break;
}
}
/**加载多条数据**/
private void loadMoreData(){
List<String>names=new ArrayList<>();
for (int i = 0; i < 10; i++) {
names.add("aa"+i);
}
//将names中的数据用DefaultAdapter2.InterItemData封装然后展示
for (int i = 0; i < names.size(); i++) {
String name=names.get(i);
DefaultAdapter2.InterItemData itemData=new DefaultAdapter2.InterItemData() {
@Override
public int getLayoutId() {
return R.layout.item_layout;
}
@Override
public void onBindView(DefaultAdapter2.ViewHolder viewHolder, int position) {
//初始化
TextView tv = (TextView) viewHolder.getView(R.id.tv);
//设置数据
String name = mItemDatas.get(position).getData().toString();
tv.setText(name);
//点击事件
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.longShow(name);
}
});
}
@Override
public <T> T getData() {
//设置数据源
return (T) name;
}
};
//添加数据
mItemDatas.add(itemData);
}
}
/**加载不同布局**/
private void loadDiffLayoutData(){
//布局一数据
DefaultAdapter2.InterItemData itemData1=new DefaultAdapter2.InterItemData() {
@Override
public int getLayoutId() {
return R.layout.item_layout;
}
@Override
public void onBindView(DefaultAdapter2.ViewHolder viewHolder, int position) {
//初始化
TextView tv = (TextView) viewHolder.getView(R.id.tv);
//设置数据
String name = mItemDatas.get(position).getData().toString();
tv.setText(name);
//点击事件
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.longShow(name + " " + position);
}
});
}
@Override
public <T> T getData() {
//设置数据源
return (T)"我是布局一";
}
};
//布局二数据
DefaultAdapter2.InterItemData itemData2=new DefaultAdapter2.InterItemData() {
@Override
public int getLayoutId() {
return R.layout.empty_layout;
}
@Override
public void onBindView(DefaultAdapter2.ViewHolder viewHolder, int position) {
//初始化
Button btn= (Button) viewHolder.getView(R.id.btn_reload);
//设置数据
String name = mItemDatas.get(position).getData().toString();
btn.setText(name);
//点击事件
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.longShow(name+"中的按钮");
}
});
//布局尺寸
ViewGroup.LayoutParams params= viewHolder.getItemHolderView().getLayoutParams();
params.height= ViewGroup.LayoutParams.WRAP_CONTENT;
viewHolder.getItemHolderView().setLayoutParams(params);
//布局点击
viewHolder.getItemHolderView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ToastUtil.longShow(name + " " + position);
}
});
}
@Override
public <T> T getData() {
//设置数据源
return (T)"我是布局二";
}
};
//添加数据
mItemDatas.add(itemData1);
mItemDatas.add(itemData2);
}
/**加载空布局**/
private void loadEmptyLayoutData(){
//加载空布局
DefaultAdapter2.InterItemData itemDataEmpty=new DefaultAdapter2.InterItemData() {
@Override
public int getLayoutId() {
return R.layout.empty_layout;
}
@Override
public void onBindView(DefaultAdapter2.ViewHolder viewHolder, int position) {
//初始化
Button btn= (Button) viewHolder.getView(R.id.btn_reload);
//设置数据
String name = mItemDatas.get(position).getData().toString();
btn.setText(name);
//点击事件
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//ToastUtil.longShow(name + " " + position);
ToastUtil.longShow("这里你可以重新获取数据");
}
});
}
@Override
public <T> T getData() {
//设置数据源
return (T)"重新加载";
}
};
//添加数据
mItemDatas.add(itemDataEmpty);
}
}
五.效果图及项目结构图
相同布局效果图
不同布局效果图
空布局效果图
项目结构图
六.DefaultAdapter2源码
DefaultAdapter2源码如下: