转载自: https://www.jianshu.com/p/66702b7a6881
RecyclerView实现多种item布局
Demo1
在项目中列表是基本都会用到的,然而在显示列表时,我们需要的数据可能需要不止一种item显示,对于复杂的数据就需要多种item,以不同的样式显示出来,这样效果是很棒的,我们先看一下效果
我们可以看到,这个RecyclerView中有多种item显示出来,那么具体怎么实现呢,其实在RecyclerView中,我们可以重写方法getItemViewType(),这个方法会传进一个参数position表示当前是第几个Item,然后我们可以通过position拿到当前的Item对象,然后判断这个item对象需要那种视图,返回一个int类型的视图标志,然后在onCreatViewHolder方法中给引入布局,这样就能够实现多种item显示了,讲了这么多我们看一下具体的例子
首先我们重写了getItemViewType这个方法,在这个方法中根据position对item对象做了一些判断,如果存储item对象的集合大小为空,返回空view标识(这里为1),如果item对象为null,返回进度条标识,这个主要是用于实现下拉加载更多,如果item对象类型属于图片类型,就返回图片类型对应的Item,这个就是效果图中的第一个Item类型,否则就是其它类型,也就是效果图中的另一种item布局,然后我们在onCreatViewHolder中具体的为每一种类型引入其布局
上面的代码就是具体为每种viewType引入其对应的布局,这样就基本实现了多种item布局,但是仅仅是这些还不够,因为我们还要对每种item设置数据,所以还要对每种item写一个VIewHolder来为item显示数据
上面就是item对应的几个ViewHolder,判断viewHolder属于那种对象,然后在onBindViewHolder中根据对应的ViewHolder对其控件设置数据并显示
整个过程基本就是这样,这种方式在项目中经常会用到,我们就可以这样去处理,下拉加载更多就可以这样实现,在加载完数据后再往对象集合中传入null,然后判断如果出现null就加载progressBar布局,再加上Google官方的SwipeRefreshLayout,下拉刷新,上拉加载就搞定了,其实很容易,而且也有点Material
Design 的感觉~~~~~~
看下Adapter的全部代码
Demo2(即时通讯对话列表)
实现多种item有许多种方式,我们这里就用RecyclerView提供给我们的方式,getItemViewType。
这个方法是RecyclerView专门开放给我们用来实现多item类型的方法,所以他是最优雅的。
我仔细看过源码,这种方式用来实现多种Item,绝对是丝毫不影响性能的,请放心使用。
以即时通讯app为例子,你肯定要有一左一右的对话框吧。
所以在Adapter中先定义这两种ViewHolder。
private static class MeViewHolder extends RecyclerView.ViewHolder {
TextView mTvMyMsg;
ImageView mIvMyAvatar;
public MeViewHolder(View view) {
super(view);
mTvMyMsg = (TextView) view.findViewById(R.id.tv_my_msg);
mIvMyAvatar = (ImageView) view.findViewById(R.id.iv_my_avatar);
}
}
private static class FriendViewHolder extends RecyclerView.ViewHolder {
TextView mTvFriendMsg;
ImageView mIvFriendAvatar;
public FriendViewHolder(View view) {
super(view);
mTvFriendMsg = (TextView) view.findViewById(R.id.tv_friend_msg);
mIvFriendAvatar = (ImageView) view.findViewById(R.id.iv_friend_avatar);
}
}
getItemViewType方法,我这里list装的是bean,每个bean里都有一个,0代表我自己的对话,1代表对方的对话。
@Override
public int getItemViewType(int position) {
return list.get(position).getMsgType();
}
onCreateViewHolder方法,一目了然
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (context == null) {
context = parent.getContext();
}
if (viewType == TYPE_ME) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_rv_chat_me, parent, false);
return new MeViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_rv_chat_friend, parent, false);
return new FriendViewHolder(view);
}
}
onBindViewHolder方法,也很简单
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof MeViewHolder) {
((MeViewHolder) holder).mTvMyMsg.setText(list.get(position).getMsgContent());
} else {
((FriendViewHolder) holder).mTvFriendMsg.setText(list.get(position).getMsgContent());
}
}
就完了!
效果图:(我这里是点击添加一个对话,点击添加一个,即时通讯这里先不实现)
全部代码
package com.example.wechat.adapter;
import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.example.wechat.R;
import com.example.wechat.bean.ChatDetailBean;
import com.example.wechat.bean.ChatItemBean;
import com.example.wechat.chat.ChatActivity;
import java.util.List;
import static com.example.wechat.R.id.view;
public class ChatDetailAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ChatDetailBean> list;
public ChatDetailAdapter(List<ChatDetailBean> list) {
this.list = list;
}
private Context context = null;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (context == null) {
context = parent.getContext();
}
if (viewType == TYPE_ME) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_rv_chat_me, parent, false);
return new MeViewHolder(view);
} else {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_rv_chat_friend, parent, false);
return new FriendViewHolder(view);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof MeViewHolder) {
((MeViewHolder) holder).mTvMyMsg.setText(list.get(position).getMsgContent());
} else {
((FriendViewHolder) holder).mTvFriendMsg.setText(list.get(position).getMsgContent());
}
}
private static final int TYPE_ME = 0;
private static final int TYPE_FRIEND = 1;
@Override
public int getItemViewType(int position) {
return list.get(position).getMsgType();
}
@Override
public int getItemCount() {
return list.size();
}
private static class MeViewHolder extends RecyclerView.ViewHolder {
TextView mTvMyMsg;
ImageView mIvMyAvatar;
public MeViewHolder(View view) {
super(view);
mTvMyMsg = (TextView) view.findViewById(R.id.tv_my_msg);
mIvMyAvatar = (ImageView) view.findViewById(R.id.iv_my_avatar);
}
}
private static class FriendViewHolder extends RecyclerView.ViewHolder {
TextView mTvFriendMsg;
ImageView mIvFriendAvatar;
public FriendViewHolder(View view) {
super(view);
mTvFriendMsg = (TextView) view.findViewById(R.id.tv_friend_msg);
mIvFriendAvatar = (ImageView) view.findViewById(R.id.iv_friend_avatar);
}
}
}
Demo3(电商app界面)
第一家公司做外包,做过不少电商项目,首页设计基本都是这个样
从上至下分别一个轮播图框,一个横向滑动的view用来展示一些特殊的列表,几个快捷菜单,和一个显示商品的列表。
实际应用里截图:
界面1
界面2
这里就用一个基本的例子来实现如何RecyclerView怎么进行多item布局。
先看效果图:
图片三
图片四
图片五
首先,轮播图的bean,横向List的bean,和正常list的bean肯定不同,而adapter只有一个与一个数组绑定,所以三种bean要可以是统一类型的bean,所以我定义了一个三种bean的父类bean就叫它ExampleBaseBean
public class ExampleBaseBean {
private int viewType;
public int getViewType() {
return viewType;
}
public void setViewType(int viewType) {
this.viewType = viewType;
}
}
如代码所示,这个BaseBean主要作用就是提供一个int类型的vieType的对象用来区分不同类型的bean。
有了BaseBean,接下来就是三种bean了。
public class TitleBean extends ExampleBaseBean {
//轮播bean,为了方便叫titleBean
private List<String> titles;//轮播的数据源一般都为数组。
public List<String> getTitles() {
return titles;
}
public void setTitles(List<String> titles) {
this.titles = titles;
}
}
public class BodyBean extends ExampleBaseBean {
//中间横向滑动的bean ,通常都有图片展示,这里用本地图片展示
private List<Integer> res;
public List<Integer> getRes() {
return res;
}
public void setRes(List<Integer> res) {
this.res = res;
}
}
public class FootBean extends ExampleBaseBean {
//正常列表的bean
private String str;
public FootBean(String str) {
this.str = str;
}
public String getStr() {
return str;
}
}
三种bean定义完,该着手adapter了。
public class ExampleAdapter extends RecyclerView.Adapter {
public final static int TITLE = 1001;//标题的viewType
public final static int BODY = 1002;//横向列表的viewType
public final static int FOOT = 1003;//正常列表的viewType
private List<ExampleBaseBean> mlist;//adapter的数据源
private Context context;
private LayoutInflater inflater;
public ExampleAdapter(List<ExampleBaseBean> mlist) {
this.mlist = mlist;
}
首先在这个adapter里将会用到viewtype写成静态常量,方便外面统一。
先写三种bean对应的ViewHolder:
private class TitleHolder extends RecyclerView.ViewHolder {
ViewPager vp;
public TitleHolder(View itemView) {
super(itemView);
vp = itemView.findViewById(R.id.vp);
}
}
private class BodyHolder extends RecyclerView.ViewHolder {
RecyclerView rv;
public BodyHolder(View itemView) {
super(itemView);
rv = itemView.findViewById(R.id.rv);
}
}
private class FootHolder extends RecyclerView.ViewHolder {
TextView tv_foot;
public FootHolder(View itemView) {
super(itemView);
tv_foot = itemView.findViewById(R.id.tv_foot);
}
}
三种ViewHolder写完了,然后就是复写RecyclerView.Adapter的getItemViewType方法来控制它返回给onCreateViewHolder的viewType:
@Override
public int getItemViewType(int position) {
if (mlist.size() > 0) {
return mlist.get(position).getViewType();
}
return super.getItemViewType(position);
}
然后就是adapter的主要方法onCreateViewHolder方法:
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (context == null)
context = parent.getContext();
if (inflater == null)
inflater = LayoutInflater.from(context);
View view;
switch (viewType) {
case TITLE:
view = inflater.inflate(R.layout.listitem_title, parent, false);
return new TitleHolder(view);
case BODY:
view = inflater.inflate(R.layout.listitem_body, parent, false);
return new BodyHolder(view);
case FOOT:
view = inflater.inflate(R.layout.listitem_foot, parent, false);
return new FootHolder(view);
}
return null;
}
根据onCreateViewHolder方法传来的viewType进行判断然后返回对应item布局的ViewHolder。
ViewHolder会被传到onBindViewHolder方法,接下来就只再要实现onBindViewHolder里就OK了。
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof TitleHolder) {
TitleBean titleBean = (TitleBean) mlist.get(position);
((TitleHolder) holder).vp.setAdapter(new PagerAdapter(titleBean.getTitles()));
}
if (holder instanceof BodyHolder) {
BodyBean bodyBean = (BodyBean) mlist.get(position);
((BodyHolder) holder).rv.setLayoutManager(new LinearLayoutManager(context, LinearLayout.HORIZONTAL, false));
((BodyHolder) holder).rv.setAdapter(new BodyAdapter(bodyBean.getRes()));
}
if (holder instanceof FootHolder) {
FootBean footBean = (FootBean) mlist.get(position);
((FootHolder) holder).tv_foot.setText(footBean.getStr());
}
}
这里的viewPager的adapter我图省事直接写成内部类了
private class PagerAdapter extends android.support.v4.view.PagerAdapter {
List<String> stringList;
public PagerAdapter(List<String> stringList) {
this.stringList = stringList;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
ViewPager.LayoutParams params = new ViewPager.LayoutParams();
params.width = ViewPager.LayoutParams.WRAP_CONTENT;
params.height = ViewPager.LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.CENTER;
TextView textView = new TextView(container.getContext());
textView.setText(stringList.get(position));
textView.setTextSize(30);
textView.setTextColor(Color.parseColor("#333333"));
textView.setLayoutParams(params);
container.addView(textView);
return textView;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
@Override
public int getCount() {
return stringList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
}
还有一个Body的adapter就是平时展示一种数据的adapter,这里也贴一下。
public class BodyAdapter extends RecyclerView.Adapter {
private List<Integer> res;
public BodyAdapter(List<Integer> res) {
this.res = res;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.listitem_body_img, parent, false);
return new ImgHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ImgHolder) {
((ImgHolder) holder).iv.setImageResource(res.get(position));
}
}
private class ImgHolder extends RecyclerView.ViewHolder {
ImageView iv;
public ImgHolder(View itemView) {
super(itemView);
iv = itemView.findViewById(R.id.iv);
}
}
@Override
public int getItemCount() {
return res.size();
}
}
还有就是Acitivy里模拟数据的添加代码:
public class ExampleActivity extends AppCompatActivity {
private ExampleAdapter adapter;
private List<ExampleBaseBean> mlist = new ArrayList<>();
private RecyclerView rv;
private Context context;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_example);
context = this;
rv = findViewById(R.id.rv);
initTitle();
initBody();
initFoot();
initAdapter();
}
private void initFoot() {
for (int i = 0; i < 10; i++) {
FootBean footBean=new FootBean("foot:"+i);
footBean.setViewType(ExampleAdapter.FOOT);//正常列表
mlist.add(footBean);
}
}
private void initBody() {
List<Integer> res = new ArrayList<>(6);
res.add(R.mipmap.pic1);
res.add(R.mipmap.pic2);
res.add(R.mipmap.pic3);
res.add(R.mipmap.pic4);
res.add(R.mipmap.pic5);
res.add(R.mipmap.pic6);
BodyBean bodyBean = new BodyBean();
bodyBean.setRes(res);
bodyBean.setViewType(ExampleAdapter.BODY);//设置横向列表的类型
mlist.add(bodyBean);
}
private void initTitle() {
List<String> titles = new ArrayList<>(5);
for (int i = 0; i < 5; i++) {
titles.add(new StringBuilder("标题").append(i).toString());
}
TitleBean titleBean = new TitleBean();
titleBean.setTitles(titles);
titleBean.setViewType(ExampleAdapter.TITLE);//设置为轮播类型
mlist.add(titleBean);
}
private void initAdapter() {
if (adapter == null) {
adapter = new ExampleAdapter(mlist);
rv.setLayoutManager(new LinearLayoutManager(context));
rv.setAdapter(adapter);
} else {
adapter.notifyDataSetChanged();
}
}
}
Item的xml布局也极其简单都是只有一个控件,就不贴了。