一种优雅的方式实现RecyclerView条目多类型

571 阅读51分钟
原文链接: blog.csdn.net

下面以一个故事来讲来说明这中方式是怎么实现的。

放弃vlayout

大家都了解一般首页是非常复杂的,去年初项目引入vlayout来解决首页复杂布局的问题,后来对vlayout和databinding结合进行了封装,使用起来更方便简单,不仅首页使用,很多页面都在用,还封装了单纯列表样式的Activity,刷新加载的Activity,这样很开心的过了很久。由于vlayout项目一直比较活跃,在满足各种各样的需求上一直在打补丁,我也是一直在把它更新为最新版本,直到又一次更新我的的列表不显示内容了,经过一上午的排查,找到了问题。是在合并一个同学的PR时引入的,当时我还提了个issue  升级后出现onBindViewHolder未分发的问题,并给作者提了建议,加强Code Review,其实这时候我就没有那么happy了。

在一次需求中,PM提出了可以删除列表中某一条目的需求,在之前封装的基础上很简单就实现了。这时想加一个移除的动画吧,让APP活泼点,不是那么生硬。这可难住了我,一上午硬是没搞出来,在别的同学的issue 怎么正确的使用notifyItemRemoved,正是这个问题,使得我有了放弃使用vlayout的想法。不禁问自己,我为什么要使用它,没错就是为了使复杂布局更方便管理,现在看来有悖于初衷。也许vlayout有删除动画的简单实现方式,而我没有找到,但是我决定不再使用它。

寻找轮子

放弃之后面临的另一个问题是需求还是要做,项目还是要按时上线,冒出了第一个想法是找找其他的轮子吧,MultiItem github上的介绍是一个优雅的实现多类型的RecyclerView类库,窃喜,这不正是我想要的。他的思想是给BaseItemAdapter(设置给RecyclerView)注册一系列的Adapter,然后根据需要处理的类来区分是要选择哪个被代理的Adapter。

[java] view plain copy print?
  1. // 初始化adapter  
  2. BaseItemAdapter adapter = new BaseItemAdapter();  
  3. // 为TextBean数据源注册ViewHolderManager管理类  
  4. adapter.register(TextBean.class, new TextViewManager());  
  5. // 为更多数据源注册ViewHolderManager管理类  
  6. adapter.register(ImageTextBean.class, new ImageAndTextManager());  
  7. adapter.register(ImageBean.class, new ImageViewManager());  
  8. // 为RecyclerView设置Adapter  
  9. recyclerView.setAdapter(adapter);  
// 初始化adapter
BaseItemAdapter adapter = new BaseItemAdapter();
// 为TextBean数据源注册ViewHolderManager管理类
adapter.register(TextBean.class, new TextViewManager());
// 为更多数据源注册ViewHolderManager管理类
adapter.register(ImageTextBean.class, new ImageAndTextManager());
adapter.register(ImageBean.class, new ImageViewManager());
// 为RecyclerView设置Adapter
recyclerView.setAdapter(adapter);

我要做的就是快速拿它匹配下我的场景,能不能满足我的需求。

  1. 是否支持多种类型条目?废话肯定支持;
  2. 是否支持不同条目不同数据类型?人家就是很久需要处理的数据类型来进行选择的,肯定没问题;
  3. 能不能支持一个数据类型对应多个样式?我看作者也是支持的,即通过数据实体中的标志来判断使用哪个Adapter;
  4. 能不能支持一个数据实体对应多个样式?由于是基于数据的类型进行选择代理Adapter的,这看来是无法实现。

我的微笑还没收场,就尴尬的定住了,5秒钟后,晃过神来,为什么我不按照这种思想自己封装一个。说实话我对作者的代理Adapter的管理还是不太满意的,这种思想很好,还是忍不住给作者点赞,那就自己来撸个轮子吧。

需求迭代

简单列表

PM在一次迭代过程中提出了要加一个新闻列表的需求,很简单,就是左边一个图片,右边一个标题、来源、发布时间,点击可以查看详情。


你一看,心中默念so easy,三下五除二,你就用RecyclerView很快实现了。

activity xml layout

[html] view plain copy print?
  1. <?xml version="1.0" encoding= "utf-8"?>  
  2. <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     tools:context="com.kevin.myapplication.MainActivity">  
  8.   
  9.     <android.support.v7.widget.RecyclerView  
  10.         android:id="@+id/recycler_view"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="match_parent"  
  13.         app:layout_constraintBottom_toBottomOf="parent"  
  14.         app:layout_constraintLeft_toLeftOf="parent"  
  15.         app:layout_constraintRight_toRightOf="parent"  
  16.         app:layout_constraintTop_toTopOf="parent" />  
  17.   
  18. </android.support.constraint.ConstraintLayout>  
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.kevin.myapplication.MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

实体对象

[java] view plain copy print?
  1. public class News {  
  2.     public String imgUrl = "";  
  3.     public String content = "";  
  4.     public String source = "";  
  5.     public String time = "";  
  6.     public String link = "";  
  7. }  
public class News {
    public String imgUrl = "";
    public String content = "";
    public String source = "";
    public String time = "";
    public String link = "";
}

adapter

[java] view plain copy print?
  1. public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {  
  2.   
  3.     private List<News> dataItems = new ArrayList<>();  
  4.   
  5.     public void setDataItems(List<News> dataItems) {  
  6.         this.dataItems = dataItems;  
  7.         notifyDataSetChanged();  
  8.     }  
  9.   
  10.     @Override  
  11.     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  12.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news, parent, false);  
  13.         ViewHolder holder = new ViewHolder(view);  
  14.         return holder;  
  15.     }  
  16.   
  17.     @Override  
  18.     public void onBindViewHolder(ViewHolder holder, int position) {  
  19.         News news = dataItems.get(position);  
  20.         holder.tvContent.setText(news.content);  
  21.         holder.tvSource.setText(news.source);  
  22.         holder.tvTime.setText(news.time);  
  23.         Glide.with(holder.itemView.getContext()).load(news.imgUrl).into(holder.ivPic);  
  24.     }  
  25.   
  26.     @Override  
  27.     public int getItemCount() {  
  28.         return dataItems.size();  
  29.     }  
  30.   
  31.     static class ViewHolder extends RecyclerView.ViewHolder {  
  32.         ImageView ivPic;  
  33.         TextView tvContent;  
  34.         TextView tvSource;  
  35.         TextView tvTime;  
  36.   
  37.         public ViewHolder(View view) {  
  38.             super(view);  
  39.             ivPic = view.findViewById(R.id.iv_pic);  
  40.             tvContent = view.findViewById(R.id.tv_content);  
  41.             tvSource = view.findViewById(R.id.tv_source);  
  42.             tvTime = view.findViewById(R.id.tv_time);  
  43.         }  
  44.     }  
  45. }  
public class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {

    private List<News> dataItems = new ArrayList<>();

    public void setDataItems(List<News> dataItems) {
        this.dataItems = dataItems;
        notifyDataSetChanged();
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news, parent, false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        News news = dataItems.get(position);
        holder.tvContent.setText(news.content);
        holder.tvSource.setText(news.source);
        holder.tvTime.setText(news.time);
        Glide.with(holder.itemView.getContext()).load(news.imgUrl).into(holder.ivPic);
    }

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

    static class ViewHolder extends RecyclerView.ViewHolder {
        ImageView ivPic;
        TextView tvContent;
        TextView tvSource;
        TextView tvTime;

        public ViewHolder(View view) {
            super(view);
            ivPic = view.findViewById(R.id.iv_pic);
            tvContent = view.findViewById(R.id.tv_content);
            tvSource = view.findViewById(R.id.tv_source);
            tvTime = view.findViewById(R.id.tv_time);
        }
    }
}

adapter item layout

[java] view plain copy print?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="wrap_content"  
  7.     android:paddingLeft="10dp"  
  8.     android:paddingRight="10dp"  
  9.     android:paddingTop="10dp">  
  10.   
  11.     <ImageView  
  12.         android:id="@+id/iv_pic"  
  13.         android:layout_width="0dp"  
  14.         android:layout_height="80dp"  
  15.         android:scaleType="centerCrop"  
  16.         app:layout_constraintHorizontal_weight="1"  
  17.         app:layout_constraintLeft_toLeftOf="parent"  
  18.         app:layout_constraintRight_toLeftOf="@+id/tv_content"  
  19.         app:layout_constraintTop_toTopOf="parent"  
  20.         tools:src="@mipmap/ic_launcher" />  
  21.   
  22.     <TextView  
  23.         android:id="@+id/tv_content"  
  24.         android:layout_width="0dp"  
  25.         android:layout_height="wrap_content"  
  26.         android:layout_marginLeft="10dp"  
  27.         android:ellipsize="end"  
  28.         android:maxLines="2"  
  29.         android:textColor="#333333"  
  30.         android:textSize="18sp"  
  31.         app:layout_constraintHorizontal_chainStyle="spread"  
  32.         app:layout_constraintHorizontal_weight="2"  
  33.         app:layout_constraintLeft_toRightOf="@+id/iv_pic"  
  34.         app:layout_constraintRight_toRightOf="parent"  
  35.         app:layout_constraintTop_toTopOf="parent"  
  36.         tools:text="这是一条新闻,这是一条新闻,这是一条新闻" />  
  37.   
  38.     <TextView  
  39.         android:id="@+id/tv_source"  
  40.         android:layout_width="wrap_content"  
  41.         android:layout_height="wrap_content"  
  42.         android:layout_marginLeft="10dp"  
  43.         android:textColor="#888888"  
  44.         android:textSize="12sp"  
  45.         app:layout_constraintBottom_toBottomOf="@+id/iv_pic"  
  46.         app:layout_constraintLeft_toRightOf="@+id/iv_pic"  
  47.         tools:text="澎湃新闻" />  
  48.   
  49.     <TextView  
  50.         android:id="@+id/tv_time"  
  51.         android:layout_width="wrap_content"  
  52.         android:layout_height="wrap_content"  
  53.         android:layout_marginLeft="8dp"  
  54.         android:textColor="#888888"  
  55.         android:textSize="12sp"  
  56.         app:layout_constraintBottom_toBottomOf="@+id/iv_pic"  
  57.         app:layout_constraintLeft_toRightOf="@+id/tv_source"  
  58.         tools:text="07:33" />  
  59.   
  60.     <View  
  61.         android:layout_width="0dp"  
  62.         android:layout_height="1px"  
  63.         android:layout_margin="10dp"  
  64.         android:background="#EEEEEE"  
  65.         app:layout_constraintTop_toBottomOf="@id/iv_pic" />  
  66.   
  67. </android.support.constraint.ConstraintLayout>  
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="10dp">

    <ImageView
        android:id="@+id/iv_pic"
        android:layout_width="0dp"
        android:layout_height="80dp"
        android:scaleType="centerCrop"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/tv_content"
        app:layout_constraintTop_toTopOf="parent"
        tools:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:ellipsize="end"
        android:maxLines="2"
        android:textColor="#333333"
        android:textSize="18sp"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintHorizontal_weight="2"
        app:layout_constraintLeft_toRightOf="@+id/iv_pic"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="这是一条新闻,这是一条新闻,这是一条新闻" />

    <TextView
        android:id="@+id/tv_source"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:textColor="#888888"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="@+id/iv_pic"
        app:layout_constraintLeft_toRightOf="@+id/iv_pic"
        tools:text="澎湃新闻" />

    <TextView
        android:id="@+id/tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:textColor="#888888"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="@+id/iv_pic"
        app:layout_constraintLeft_toRightOf="@+id/tv_source"
        tools:text="07:33" />

    <View
        android:layout_width="0dp"
        android:layout_height="1px"
        android:layout_margin="10dp"
        android:background="#EEEEEE"
        app:layout_constraintTop_toBottomOf="@id/iv_pic" />

</android.support.constraint.ConstraintLayout>

看一下实现,还不错的样子。


扩充样式

某天产品经理找到你说,只有一张图片的看着太单调了,能不能扩充出另外一种样式,一张图片的时候还是原来的样子,如果三张图片的时候上面是标题,下面是图片。你觉得没什么,也比较好实现,就没有做任何反抗去做了。


应该是这样,在之前一个图片的ViewHolder基础上扩展一个三个图片的ViewHoder,通过实体对象的图片数量进行区分是选择哪一个ViewHolder,不同的ViewHolder绑定不同的数据。

修改实体对象

把原来的String类型的图片数据改为List<String>的集合。

[java] view plain copy print?
  1. public class News {  
  2.     public List<String> imgUrls = null;  
  3.     public String content = "";  
  4.     public String source = "";  
  5.     public String time = "";  
  6.     public String link = "";  
  7. }  
public class News {
    public List<String> imgUrls = null;
    public String content = "";
    public String source = "";
    public String time = "";
    public String link = "";
}

新样式的xml layout

[html] view plain copy print?
  1. <?xml version="1.0" encoding= "utf-8"?>  
  2. <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="wrap_content"  
  7.     android:paddingLeft="10dp"  
  8.     android:paddingRight="10dp"  
  9.     android:paddingTop="10dp">  
  10.   
  11.     <TextView  
  12.         android:id="@+id/tv_content"  
  13.         android:layout_width="0dp"  
  14.         android:layout_height="wrap_content"  
  15.         android:ellipsize="end"  
  16.         android:maxLines="2"  
  17.         android:textColor="#333333"  
  18.         android:textSize="18sp"  
  19.         app:layout_constraintLeft_toLeftOf="parent"  
  20.         app:layout_constraintRight_toRightOf="parent"  
  21.         app:layout_constraintTop_toTopOf="parent"  
  22.         tools:text="这是一条新闻,这是一条新闻,这是一条新闻" />  
  23.   
  24.     <ImageView  
  25.         android:id="@+id/iv_pic1"  
  26.         android:layout_width="0dp"  
  27.         android:layout_height="80dp"  
  28.         android:layout_marginTop="10dp"  
  29.         android:scaleType="centerCrop"  
  30.         app:layout_constraintHorizontal_weight="1"  
  31.         app:layout_constraintLeft_toLeftOf="parent"  
  32.         app:layout_constraintRight_toLeftOf="@+id/iv_pic2"  
  33.         app:layout_constraintTop_toBottomOf="@+id/tv_content"  
  34.         tools:src="@mipmap/ic_launcher" />  
  35.   
  36.     <ImageView  
  37.         android:id="@+id/iv_pic2"  
  38.         android:layout_width="0dp"  
  39.         android:layout_height="80dp"  
  40.         android:layout_marginLeft="4dp"  
  41.         android:layout_marginTop="10dp"  
  42.         android:scaleType="centerCrop"  
  43.         app:layout_constraintHorizontal_weight="1"  
  44.         app:layout_constraintLeft_toRightOf="@+id/iv_pic1"  
  45.         app:layout_constraintRight_toLeftOf="@+id/iv_pic3"  
  46.         app:layout_constraintTop_toBottomOf="@+id/tv_content"  
  47.         tools:src="@mipmap/ic_launcher" />  
  48.   
  49.     <ImageView  
  50.         android:id="@+id/iv_pic3"  
  51.         android:layout_width="0dp"  
  52.         android:layout_height="80dp"  
  53.         android:layout_marginLeft="4dp"  
  54.         android:layout_marginTop="10dp"  
  55.         android:scaleType="centerCrop"  
  56.         app:layout_constraintHorizontal_weight="1"  
  57.         app:layout_constraintLeft_toRightOf="@+id/iv_pic2"  
  58.         app:layout_constraintRight_toRightOf="parent"  
  59.         app:layout_constraintTop_toBottomOf="@+id/tv_content"  
  60.         tools:src="@mipmap/ic_launcher" />  
  61.   
  62.     <TextView  
  63.         android:id="@+id/tv_source"  
  64.         android:layout_width="wrap_content"  
  65.         android:layout_height="wrap_content"  
  66.         android:layout_marginTop="10dp"  
  67.         android:textColor="#888888"  
  68.         android:textSize="12sp"  
  69.         app:layout_constraintLeft_toLeftOf="parent"  
  70.         app:layout_constraintTop_toBottomOf="@+id/iv_pic1"  
  71.         tools:text="澎湃新闻" />  
  72.   
  73.     <TextView  
  74.         android:id="@+id/tv_time"  
  75.         android:layout_width="wrap_content"  
  76.         android:layout_height="wrap_content"  
  77.         android:layout_marginLeft="8dp"  
  78.         android:textColor="#888888"  
  79.         android:textSize="12sp"  
  80.         app:layout_constraintLeft_toRightOf="@+id/tv_source"  
  81.         app:layout_constraintTop_toTopOf="@+id/tv_source"  
  82.         tools:text="07:33" />  
  83.   
  84.     <View  
  85.         android:layout_width="0dp"  
  86.         android:layout_height="1px"  
  87.         android:layout_margin="10dp"  
  88.         android:background="#EEEEEE"  
  89.         app:layout_constraintTop_toBottomOf="@id/tv_source" />  
  90.   
  91. </android.support.constraint.ConstraintLayout>  
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="10dp">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:maxLines="2"
        android:textColor="#333333"
        android:textSize="18sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="这是一条新闻,这是一条新闻,这是一条新闻" />

    <ImageView
        android:id="@+id/iv_pic1"
        android:layout_width="0dp"
        android:layout_height="80dp"
        android:layout_marginTop="10dp"
        android:scaleType="centerCrop"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/iv_pic2"
        app:layout_constraintTop_toBottomOf="@+id/tv_content"
        tools:src="@mipmap/ic_launcher" />

    <ImageView
        android:id="@+id/iv_pic2"
        android:layout_width="0dp"
        android:layout_height="80dp"
        android:layout_marginLeft="4dp"
        android:layout_marginTop="10dp"
        android:scaleType="centerCrop"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toRightOf="@+id/iv_pic1"
        app:layout_constraintRight_toLeftOf="@+id/iv_pic3"
        app:layout_constraintTop_toBottomOf="@+id/tv_content"
        tools:src="@mipmap/ic_launcher" />

    <ImageView
        android:id="@+id/iv_pic3"
        android:layout_width="0dp"
        android:layout_height="80dp"
        android:layout_marginLeft="4dp"
        android:layout_marginTop="10dp"
        android:scaleType="centerCrop"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toRightOf="@+id/iv_pic2"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_content"
        tools:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/tv_source"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColor="#888888"
        android:textSize="12sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/iv_pic1"
        tools:text="澎湃新闻" />

    <TextView
        android:id="@+id/tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:textColor="#888888"
        android:textSize="12sp"
        app:layout_constraintLeft_toRightOf="@+id/tv_source"
        app:layout_constraintTop_toTopOf="@+id/tv_source"
        tools:text="07:33" />

    <View
        android:layout_width="0dp"
        android:layout_height="1px"
        android:layout_margin="10dp"
        android:background="#EEEEEE"
        app:layout_constraintTop_toBottomOf="@id/tv_source" />

</android.support.constraint.ConstraintLayout>

Adapter改造

复写getItemViewType方法,通过判断图片的个数是不是3个来区分样式。如果是则ViewType为1,如果不是则ViewType为0。

[java] view plain copy print?
  1. @Override  
  2. public int getItemViewType(int position) {  
  3.     News news = dataItems.get(position);  
  4.     boolean isThreePic = news.imgUrls.size() == 3;  
  5.     int viewType = isThreePic ? 1 : 0;  
  6.     return viewType;  
  7. }  
@Override
public int getItemViewType(int position) {
    News news = dataItems.get(position);
    boolean isThreePic = news.imgUrls.size() == 3;
    int viewType = isThreePic ? 1 : 0;
    return viewType;
}

增加三张图片样式的ViewHolder

[java] view plain copy print?
  1. static class ThreePicViewHolder extends RecyclerView.ViewHolder {  
  2.     ImageView ivPic1;  
  3.     ImageView ivPic2;  
  4.     ImageView ivPic3;  
  5.     TextView tvContent;  
  6.     TextView tvSource;  
  7.     TextView tvTime;  
  8.   
  9.     public ThreePicViewHolder(View view) {  
  10.         super(view);  
  11.         ivPic1 = view.findViewById(R.id.iv_pic1);  
  12.         ivPic2 = view.findViewById(R.id.iv_pic2);  
  13.         ivPic3 = view.findViewById(R.id.iv_pic3);  
  14.         tvContent = view.findViewById(R.id.tv_content);  
  15.         tvSource = view.findViewById(R.id.tv_source);  
  16.         tvTime = view.findViewById(R.id.tv_time);  
  17.     }  
  18. }  
static class ThreePicViewHolder extends RecyclerView.ViewHolder {
    ImageView ivPic1;
    ImageView ivPic2;
    ImageView ivPic3;
    TextView tvContent;
    TextView tvSource;
    TextView tvTime;

    public ThreePicViewHolder(View view) {
        super(view);
        ivPic1 = view.findViewById(R.id.iv_pic1);
        ivPic2 = view.findViewById(R.id.iv_pic2);
        ivPic3 = view.findViewById(R.id.iv_pic3);
        tvContent = view.findViewById(R.id.tv_content);
        tvSource = view.findViewById(R.id.tv_source);
        tvTime = view.findViewById(R.id.tv_time);
    }
}

在onCreateViewHolder中,通过判断ViewType的类型,创建对应的ViewHolder。

[java] view plain copy print?
  1. @Override  
  2. public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  3.     if (viewType == 0) {  
  4.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news, parent, false);  
  5.         OnePicViewHolder holder = new OnePicViewHolder(view);  
  6.         return holder;  
  7.     } else {  
  8.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_extend, parent, false);  
  9.         ThreePicViewHolder holder = new ThreePicViewHolder(view);  
  10.         return holder;  
  11.     }  
  12. }  
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == 0) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news, parent, false);
        OnePicViewHolder holder = new OnePicViewHolder(view);
        return holder;
    } else {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_extend, parent, false);
        ThreePicViewHolder holder = new ThreePicViewHolder(view);
        return holder;
    }
}

在onBindingViewHolder中,通过判断ViewType的类型,绑定对应的数据到控件。

[java] view plain copy print?
  1. @Override  
  2. public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {  
  3.     int viewType = holder.getItemViewType();  
  4.     if (viewType == 0) {  
  5.         OnePicViewHolder viewHolder = (OnePicViewHolder) holder;  
  6.         News news = dataItems.get(position);  
  7.         viewHolder.tvContent.setText(news.content);  
  8.         viewHolder.tvSource.setText(news.source);  
  9.         viewHolder.tvTime.setText(news.time);  
  10.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);  
  11.     } else {  
  12.         ThreePicViewHolder viewHolder = (ThreePicViewHolder) holder;  
  13.         News news = dataItems.get(position);  
  14.         viewHolder.tvContent.setText(news.content);  
  15.         viewHolder.tvSource.setText(news.source);  
  16.         viewHolder.tvTime.setText(news.time);  
  17.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic1);  
  18.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(1)).into(viewHolder.ivPic2);  
  19.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(2)).into(viewHolder.ivPic3);  
  20.     }  
  21. }  
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    int viewType = holder.getItemViewType();
    if (viewType == 0) {
        OnePicViewHolder viewHolder = (OnePicViewHolder) holder;
        News news = dataItems.get(position);
        viewHolder.tvContent.setText(news.content);
        viewHolder.tvSource.setText(news.source);
        viewHolder.tvTime.setText(news.time);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);
    } else {
        ThreePicViewHolder viewHolder = (ThreePicViewHolder) holder;
        News news = dataItems.get(position);
        viewHolder.tvContent.setText(news.content);
        viewHolder.tvSource.setText(news.source);
        viewHolder.tvTime.setText(news.time);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic1);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(1)).into(viewHolder.ivPic2);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(2)).into(viewHolder.ivPic3);
    }
}

OK,很快就完成了,这次的需求,看了下效果,还可以。但是没有第一次那么开心了,隐隐感觉这里的代码有点恶心。


又扩充样式

过了一段时间,产品又找到你,说想增加一种图集类型的样式,上面是标题,下面是一张大图,点开是可以左右滑动翻页的图集。说完一张呆萌脸问你是不是很简单,你想到又要加一种类型的ViewHolder,而之前的实现已经让你不爽。你说:倒是不复杂,但是你也不能无限制的加啊。抱怨完了,还是要做的。

添加xml布局

[html] view plain copy print?
  1. <?xml version="1.0" encoding= "utf-8"?>  
  2. <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="wrap_content"  
  7.     android:paddingLeft="10dp"  
  8.     android:paddingRight="10dp"  
  9.     android:paddingTop="10dp">  
  10.   
  11.     <TextView  
  12.         android:id="@+id/tv_content"  
  13.         android:layout_width="0dp"  
  14.         android:layout_height="wrap_content"  
  15.         android:ellipsize="end"  
  16.         android:maxLines="2"  
  17.         android:textColor="#333333"  
  18.         android:textSize="18sp"  
  19.         app:layout_constraintLeft_toLeftOf="parent"  
  20.         app:layout_constraintRight_toRightOf="parent"  
  21.         app:layout_constraintTop_toTopOf="parent"  
  22.         tools:text="这是一条新闻,这是一条新闻,这是一条新闻" />  
  23.   
  24.     <ImageView  
  25.         android:id="@+id/iv_pic"  
  26.         android:layout_width="0dp"  
  27.         android:layout_height="0dp"  
  28.         android:layout_marginTop="10dp"  
  29.         android:scaleType="centerCrop"  
  30.         app:layout_constraintDimensionRatio="5:3"  
  31.         app:layout_constraintLeft_toLeftOf="parent"  
  32.         app:layout_constraintRight_toRightOf="parent"  
  33.         app:layout_constraintTop_toBottomOf="@+id/tv_content"  
  34.         tools:src="@mipmap/ic_launcher" />  
  35.   
  36.     <TextView  
  37.         android:id="@+id/tv_count"  
  38.         android:layout_width="wrap_content"  
  39.         android:layout_height="wrap_content"  
  40.         android:drawableLeft="@mipmap/icon_img"  
  41.         android:drawablePadding="8dp"  
  42.         android:paddingBottom="6dp"  
  43.         android:paddingRight="10dp"  
  44.         android:textColor="@android:color/white"  
  45.         app:layout_constraintBottom_toBottomOf="@+id/iv_pic"  
  46.         app:layout_constraintRight_toRightOf="@+id/iv_pic"  
  47.         tools:text="图3" />  
  48.   
  49.     <TextView  
  50.         android:id="@+id/tv_source"  
  51.         android:layout_width="wrap_content"  
  52.         android:layout_height="wrap_content"  
  53.         android:layout_marginTop="10dp"  
  54.         android:textColor="#888888"  
  55.         android:textSize="12sp"  
  56.         app:layout_constraintLeft_toLeftOf="parent"  
  57.         app:layout_constraintTop_toBottomOf="@+id/iv_pic"  
  58.         tools:text="澎湃新闻" />  
  59.   
  60.     <TextView  
  61.         android:id="@+id/tv_time"  
  62.         android:layout_width="wrap_content"  
  63.         android:layout_height="wrap_content"  
  64.         android:layout_marginLeft="8dp"  
  65.         android:textColor="#888888"  
  66.         android:textSize="12sp"  
  67.         app:layout_constraintLeft_toRightOf="@+id/tv_source"  
  68.         app:layout_constraintTop_toTopOf="@+id/tv_source"  
  69.         tools:text="07:33" />  
  70.   
  71.     <View  
  72.         android:layout_width="0dp"  
  73.         android:layout_height="1px"  
  74.         android:layout_margin="10dp"  
  75.         android:background="#EEEEEE"  
  76.         app:layout_constraintTop_toBottomOf="@id/tv_source" />  
  77.   
  78. </android.support.constraint.ConstraintLayout>  
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="10dp">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:maxLines="2"
        android:textColor="#333333"
        android:textSize="18sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="这是一条新闻,这是一条新闻,这是一条新闻" />

    <ImageView
        android:id="@+id/iv_pic"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="10dp"
        android:scaleType="centerCrop"
        app:layout_constraintDimensionRatio="5:3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_content"
        tools:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/tv_count"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableLeft="@mipmap/icon_img"
        android:drawablePadding="8dp"
        android:paddingBottom="6dp"
        android:paddingRight="10dp"
        android:textColor="@android:color/white"
        app:layout_constraintBottom_toBottomOf="@+id/iv_pic"
        app:layout_constraintRight_toRightOf="@+id/iv_pic"
        tools:text="图3" />

    <TextView
        android:id="@+id/tv_source"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColor="#888888"
        android:textSize="12sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/iv_pic"
        tools:text="澎湃新闻" />

    <TextView
        android:id="@+id/tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:textColor="#888888"
        android:textSize="12sp"
        app:layout_constraintLeft_toRightOf="@+id/tv_source"
        app:layout_constraintTop_toTopOf="@+id/tv_source"
        tools:text="07:33" />

    <View
        android:layout_width="0dp"
        android:layout_height="1px"
        android:layout_margin="10dp"
        android:background="#EEEEEE"
        app:layout_constraintTop_toBottomOf="@id/tv_source" />

</android.support.constraint.ConstraintLayout>

又改造Adapter

复写getItemViewType方法,通过判断图片的个数区分样式。

[java] view plain copy print?
  1. @Override  
  2. public int getItemViewType(int position) {  
  3.     News news = dataItems.get(position);  
  4.     int imgSize = news.imgUrls.size();  
  5.     if (imgSize < 3) {  
  6.         return 0;  
  7.     } else if (imgSize == 3) {  
  8.         return 2;  
  9.     } else {  
  10.         return 3;  
  11.     }  
  12. }  
@Override
public int getItemViewType(int position) {
    News news = dataItems.get(position);
    int imgSize = news.imgUrls.size();
    if (imgSize < 3) {
        return 0;
    } else if (imgSize == 3) {
        return 2;
    } else {
        return 3;
    }
}

增加图集样式的ViewHolder

[java] view plain copy print?
  1. static class MorePicViewHolder extends RecyclerView.ViewHolder {  
  2.     ImageView ivPic;  
  3.     TextView tvContent;  
  4.     TextView tvCount;  
  5.     TextView tvSource;  
  6.     TextView tvTime;  
  7.   
  8.     public MorePicViewHolder(View view) {  
  9.         super(view);  
  10.         ivPic = view.findViewById(R.id.iv_pic);  
  11.         tvContent = view.findViewById(R.id.tv_content);  
  12.         tvCount = view.findViewById(R.id.tv_count);  
  13.         tvSource = view.findViewById(R.id.tv_source);  
  14.         tvTime = view.findViewById(R.id.tv_time);  
  15.     }  
  16. }  
static class MorePicViewHolder extends RecyclerView.ViewHolder {
    ImageView ivPic;
    TextView tvContent;
    TextView tvCount;
    TextView tvSource;
    TextView tvTime;

    public MorePicViewHolder(View view) {
        super(view);
        ivPic = view.findViewById(R.id.iv_pic);
        tvContent = view.findViewById(R.id.tv_content);
        tvCount = view.findViewById(R.id.tv_count);
        tvSource = view.findViewById(R.id.tv_source);
        tvTime = view.findViewById(R.id.tv_time);
    }
}

在onCreateViewHolder中,通过判断ViewType的类型,创建对应的ViewHolder。

[java] view plain copy print?
  1. @Override  
  2. public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  3.     if (viewType == 0) {  
  4.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_one_pic, parent, false);  
  5.         OnePicViewHolder holder = new OnePicViewHolder(view);  
  6.         return holder;  
  7.     } else if (viewType == 2) {  
  8.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_three_pic, parent, false);  
  9.         ThreePicViewHolder holder = new ThreePicViewHolder(view);  
  10.         return holder;  
  11.     } else if (viewType == 3) {  
  12.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_more_pic, parent, false);  
  13.         MorePicViewHolder holder = new MorePicViewHolder(view);  
  14.         return holder;  
  15.     } else {  
  16.         // Can't reach;  
  17.         return null;  
  18.     }  
  19. }  
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == 0) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_one_pic, parent, false);
        OnePicViewHolder holder = new OnePicViewHolder(view);
        return holder;
    } else if (viewType == 2) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_three_pic, parent, false);
        ThreePicViewHolder holder = new ThreePicViewHolder(view);
        return holder;
    } else if (viewType == 3) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_more_pic, parent, false);
        MorePicViewHolder holder = new MorePicViewHolder(view);
        return holder;
    } else {
        // Can't reach;
        return null;
    }
}

在onBindingViewHolder中,通过判断ViewType的类型,绑定对应的数据到控件。

[java] view plain copy print?
  1. @Override  
  2. public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {  
  3.     int viewType = holder.getItemViewType();  
  4.     if (viewType == 0) {  
  5.         OnePicViewHolder viewHolder = (OnePicViewHolder) holder;  
  6.         News news = dataItems.get(position);  
  7.         viewHolder.tvContent.setText(news.content);  
  8.         viewHolder.tvSource.setText(news.source);  
  9.         viewHolder.tvTime.setText(news.time);  
  10.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);  
  11.     } else if (viewType == 2) {  
  12.         ThreePicViewHolder viewHolder = (ThreePicViewHolder) holder;  
  13.         News news = dataItems.get(position);  
  14.         viewHolder.tvContent.setText(news.content);  
  15.         viewHolder.tvSource.setText(news.source);  
  16.         viewHolder.tvTime.setText(news.time);  
  17.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic1);  
  18.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(1)).into(viewHolder.ivPic2);  
  19.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(2)).into(viewHolder.ivPic3);  
  20.     } else if (viewType == 3) {  
  21.         MorePicViewHolder viewHolder = (MorePicViewHolder) holder;  
  22.         News news = dataItems.get(position);  
  23.         viewHolder.tvContent.setText(news.content);  
  24.         viewHolder.tvSource.setText(news.source);  
  25.         viewHolder.tvTime.setText(news.time);  
  26.         viewHolder.tvCount.setText(news.count + " 图");  
  27.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);  
  28.     }  
  29. }  
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    int viewType = holder.getItemViewType();
    if (viewType == 0) {
        OnePicViewHolder viewHolder = (OnePicViewHolder) holder;
        News news = dataItems.get(position);
        viewHolder.tvContent.setText(news.content);
        viewHolder.tvSource.setText(news.source);
        viewHolder.tvTime.setText(news.time);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);
    } else if (viewType == 2) {
        ThreePicViewHolder viewHolder = (ThreePicViewHolder) holder;
        News news = dataItems.get(position);
        viewHolder.tvContent.setText(news.content);
        viewHolder.tvSource.setText(news.source);
        viewHolder.tvTime.setText(news.time);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic1);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(1)).into(viewHolder.ivPic2);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(2)).into(viewHolder.ivPic3);
    } else if (viewType == 3) {
        MorePicViewHolder viewHolder = (MorePicViewHolder) holder;
        News news = dataItems.get(position);
        viewHolder.tvContent.setText(news.content);
        viewHolder.tvSource.setText(news.source);
        viewHolder.tvTime.setText(news.time);
        viewHolder.tvCount.setText(news.count + " 图");
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);
    }
}

OK,很快又改完了,这次你的心情不太愉快,之前的太不利于扩展了,每次添加一种新的都要把Adapter改一遍。


反思

在上次需求上线之后,你开始反思,为什么我这么反感产品扩充样式,而不是我的代码有一定的业务兼容性,而是把自己搞的这么被动。算了,下楼喝杯咖啡冷静冷静。你顺便叫了下旁边的哥们,他说:“我比较忙,你给我带杯吧。”,“带什么?”你问,“摩卡小杯”。你心想,本来叫你一块儿去的,你小子这么懒叫我给你带。唉,对啊,我的Adapter不就可以这样嘛,自己不做处理,委托给其他的Adapter去做。

一开始的时候注册三个的委托Adapter对象,A委托Adapter可以处理一张图片的样式,B委托Adapter可以处理三张图片的样式,C委托可以处理多张图片的样式。根据数据里面图片的数量选择对应的委托Adapter去处理就可以啦。

委托


所有的委托都有相同的方法,为了方便定义一个接口。

[java] view plain copy print?
  1. public interface IDelegateAdapter {  
  2.   
  3.     // 查找委托时调用的方法,返回自己能处理的类型即可。  
  4.     boolean isForViewType(News news);  
  5.   
  6.     // 用于委托Adapter的onCreateViewHolder方法  
  7.     RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);  
  8.   
  9.     // 用于委托Adapter的onBindViewHolder方法  
  10.     void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news);  
  11.   
  12. }  
public interface IDelegateAdapter {

    // 查找委托时调用的方法,返回自己能处理的类型即可。
    boolean isForViewType(News news);

    // 用于委托Adapter的onCreateViewHolder方法
    RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

    // 用于委托Adapter的onBindViewHolder方法
    void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news);

}

然后是三个委托

[java] view plain copy print?
  1. public class OnePicDelegateAdapter implements IDelegateAdapter {  
  2.   
  3.     @Override  
  4.     public boolean isForViewType(News news) {  
  5.         // 我能处理一张图片  
  6.         return news.imgUrls.size() == 1;  
  7.     }  
  8.   
  9.     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  10.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_one_pic, parent, false);  
  11.         OnePicViewHolder holder = new OnePicViewHolder(view);  
  12.         return holder;  
  13.     }  
  14.   
  15.     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news) {  
  16.         OnePicViewHolder viewHolder = (OnePicViewHolder) holder;  
  17.         viewHolder.tvContent.setText(news.content);  
  18.         viewHolder.tvSource.setText(news.source);  
  19.         viewHolder.tvTime.setText(news.time);  
  20.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);  
  21.     }  
  22.   
  23.     static class OnePicViewHolder extends RecyclerView.ViewHolder {  
  24.         ImageView ivPic;  
  25.         TextView tvContent;  
  26.         TextView tvSource;  
  27.         TextView tvTime;  
  28.   
  29.         public OnePicViewHolder(View view) {  
  30.             super(view);  
  31.             ivPic = view.findViewById(R.id.iv_pic);  
  32.             tvContent = view.findViewById(R.id.tv_content);  
  33.             tvSource = view.findViewById(R.id.tv_source);  
  34.             tvTime = view.findViewById(R.id.tv_time);  
  35.         }  
  36.     }  
  37. }  
public class OnePicDelegateAdapter implements IDelegateAdapter {

    @Override
    public boolean isForViewType(News news) {
        // 我能处理一张图片
        return news.imgUrls.size() == 1;
    }

    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_one_pic, parent, false);
        OnePicViewHolder holder = new OnePicViewHolder(view);
        return holder;
    }

    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news) {
        OnePicViewHolder viewHolder = (OnePicViewHolder) holder;
        viewHolder.tvContent.setText(news.content);
        viewHolder.tvSource.setText(news.source);
        viewHolder.tvTime.setText(news.time);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);
    }

    static class OnePicViewHolder extends RecyclerView.ViewHolder {
        ImageView ivPic;
        TextView tvContent;
        TextView tvSource;
        TextView tvTime;

        public OnePicViewHolder(View view) {
            super(view);
            ivPic = view.findViewById(R.id.iv_pic);
            tvContent = view.findViewById(R.id.tv_content);
            tvSource = view.findViewById(R.id.tv_source);
            tvTime = view.findViewById(R.id.tv_time);
        }
    }
}
[java] view plain copy print?
  1. public class ThreePicDelegateAdapter implements IDelegateAdapter {  
  2.   
  3.     @Override  
  4.     public boolean isForViewType(News news) {  
  5.         // 我能处理三张图片  
  6.         return news.imgUrls.size() == 3;  
  7.     }  
  8.   
  9.     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  10.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_three_pic, parent, false);  
  11.         ThreePicViewHolder holder = new ThreePicViewHolder(view);  
  12.         return holder;  
  13.     }  
  14.   
  15.     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news) {  
  16.         ThreePicViewHolder viewHolder = (ThreePicViewHolder) holder;  
  17.         viewHolder.tvContent.setText(news.content);  
  18.         viewHolder.tvSource.setText(news.source);  
  19.         viewHolder.tvTime.setText(news.time);  
  20.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic1);  
  21.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(1)).into(viewHolder.ivPic2);  
  22.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(2)).into(viewHolder.ivPic3);  
  23.     }  
  24.   
  25.     static class ThreePicViewHolder extends RecyclerView.ViewHolder {  
  26.         ImageView ivPic1;  
  27.         ImageView ivPic2;  
  28.         ImageView ivPic3;  
  29.         TextView tvContent;  
  30.         TextView tvSource;  
  31.         TextView tvTime;  
  32.   
  33.         public ThreePicViewHolder(View view) {  
  34.             super(view);  
  35.             ivPic1 = view.findViewById(R.id.iv_pic1);  
  36.             ivPic2 = view.findViewById(R.id.iv_pic2);  
  37.             ivPic3 = view.findViewById(R.id.iv_pic3);  
  38.             tvContent = view.findViewById(R.id.tv_content);  
  39.             tvSource = view.findViewById(R.id.tv_source);  
  40.             tvTime = view.findViewById(R.id.tv_time);  
  41.         }  
  42.     }  
  43.   
  44. }  
public class ThreePicDelegateAdapter implements IDelegateAdapter {

    @Override
    public boolean isForViewType(News news) {
        // 我能处理三张图片
        return news.imgUrls.size() == 3;
    }

    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_three_pic, parent, false);
        ThreePicViewHolder holder = new ThreePicViewHolder(view);
        return holder;
    }

    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news) {
        ThreePicViewHolder viewHolder = (ThreePicViewHolder) holder;
        viewHolder.tvContent.setText(news.content);
        viewHolder.tvSource.setText(news.source);
        viewHolder.tvTime.setText(news.time);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic1);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(1)).into(viewHolder.ivPic2);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(2)).into(viewHolder.ivPic3);
    }

    static class ThreePicViewHolder extends RecyclerView.ViewHolder {
        ImageView ivPic1;
        ImageView ivPic2;
        ImageView ivPic3;
        TextView tvContent;
        TextView tvSource;
        TextView tvTime;

        public ThreePicViewHolder(View view) {
            super(view);
            ivPic1 = view.findViewById(R.id.iv_pic1);
            ivPic2 = view.findViewById(R.id.iv_pic2);
            ivPic3 = view.findViewById(R.id.iv_pic3);
            tvContent = view.findViewById(R.id.tv_content);
            tvSource = view.findViewById(R.id.tv_source);
            tvTime = view.findViewById(R.id.tv_time);
        }
    }

}
[java] view plain copy print?
  1. public class MorePicDelegateAdapter implements IDelegateAdapter {  
  2.   
  3.     @Override  
  4.     public boolean isForViewType(News news) {  
  5.         // 我能处理多张图片  
  6.         return news.imgUrls.size() > 3;  
  7.     }  
  8.   
  9.     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  10.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_more_pic, parent, false);  
  11.         MorePicViewHolder holder = new MorePicViewHolder(view);  
  12.         return holder;  
  13.     }  
  14.   
  15.     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news) {  
  16.         MorePicViewHolder viewHolder = (MorePicViewHolder) holder;  
  17.         viewHolder.tvContent.setText(news.content);  
  18.         viewHolder.tvSource.setText(news.source);  
  19.         viewHolder.tvTime.setText(news.time);  
  20.         viewHolder.tvCount.setText(news.count + " 图");  
  21.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);  
  22.     }  
  23.   
  24.     static class MorePicViewHolder extends RecyclerView.ViewHolder {  
  25.         ImageView ivPic;  
  26.         TextView tvContent;  
  27.         TextView tvCount;  
  28.         TextView tvSource;  
  29.         TextView tvTime;  
  30.   
  31.         public MorePicViewHolder(View view) {  
  32.             super(view);  
  33.             ivPic = view.findViewById(R.id.iv_pic);  
  34.             tvContent = view.findViewById(R.id.tv_content);  
  35.             tvCount = view.findViewById(R.id.tv_count);  
  36.             tvSource = view.findViewById(R.id.tv_source);  
  37.             tvTime = view.findViewById(R.id.tv_time);  
  38.         }  
  39.     }  
  40.   
  41. }  
public class MorePicDelegateAdapter implements IDelegateAdapter {

    @Override
    public boolean isForViewType(News news) {
        // 我能处理多张图片
        return news.imgUrls.size() > 3;
    }

    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_more_pic, parent, false);
        MorePicViewHolder holder = new MorePicViewHolder(view);
        return holder;
    }

    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news) {
        MorePicViewHolder viewHolder = (MorePicViewHolder) holder;
        viewHolder.tvContent.setText(news.content);
        viewHolder.tvSource.setText(news.source);
        viewHolder.tvTime.setText(news.time);
        viewHolder.tvCount.setText(news.count + " 图");
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);
    }

    static class MorePicViewHolder extends RecyclerView.ViewHolder {
        ImageView ivPic;
        TextView tvContent;
        TextView tvCount;
        TextView tvSource;
        TextView tvTime;

        public MorePicViewHolder(View view) {
            super(view);
            ivPic = view.findViewById(R.id.iv_pic);
            tvContent = view.findViewById(R.id.tv_content);
            tvCount = view.findViewById(R.id.tv_count);
            tvSource = view.findViewById(R.id.tv_source);
            tvTime = view.findViewById(R.id.tv_time);
        }
    }

}

再看下之前冗余的Adapter,现在已经非常清瘦了

[java] view plain copy print ?
  1. public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {  
  2.   
  3.     private List<News> dataItems = new ArrayList<>();  
  4.   
  5.     public void setDataItems(List<News> dataItems) {  
  6.         this.dataItems = dataItems;  
  7.         notifyDataSetChanged();  
  8.     }  
  9.   
  10.     List<IDelegateAdapter> delegateAdapters = new ArrayList<>();  
  11.   
  12.     public void addDelegate(IDelegateAdapter delegateAdapter) {  
  13.         delegateAdapters.add(delegateAdapter);  
  14.     }  
  15.   
  16.     @Override  
  17.     public int getItemViewType(int position) {  
  18.   
  19.     }  
  20.   
  21.     @Override  
  22.     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  23.   
  24.     }  
  25.   
  26.     @Override  
  27.     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {  
  28.   
  29.     }  
  30.   
  31.     @Override  
  32.     public int getItemCount() {  
  33.         return dataItems.size();  
  34.     }  
  35.   
  36. }  
public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private List<News> dataItems = new ArrayList<>();

    public void setDataItems(List<News> dataItems) {
        this.dataItems = dataItems;
        notifyDataSetChanged();
    }

    List<IDelegateAdapter> delegateAdapters = new ArrayList<>();

    public void addDelegate(IDelegateAdapter delegateAdapter) {
        delegateAdapters.add(delegateAdapter);
    }

    @Override
    public int getItemViewType(int position) {

    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    }

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

}

有三个方法需要我们去填空,在之前的图中我们说过,getItemViewType通过委托在集合中的index去标识。

[java] view plain copy print ?
  1. @Override  
  2. public int getItemViewType(int position) {  
  3.     // 找到当前位置的数据  
  4.     News news = dataItems.get(position);  
  5.   
  6.     // 遍历所有的代理,问下他们谁能处理  
  7.     for (IDelegateAdapter delegateAdapter : delegateAdapters) {  
  8.         if (delegateAdapter.isForViewType(news)) {  
  9.             // 谁能处理返回他的index  
  10.             return delegateAdapters.indexOf(delegateAdapter);  
  11.         }  
  12.     }  
  13.   
  14.     throw new RuntimeException("没有找到可以处理的委托Adapter");  
  15. }  
@Override
public int getItemViewType(int position) {
    // 找到当前位置的数据
    News news = dataItems.get(position);

    // 遍历所有的代理,问下他们谁能处理
    for (IDelegateAdapter delegateAdapter : delegateAdapters) {
        if (delegateAdapter.isForViewType(news)) {
            // 谁能处理返回他的index
            return delegateAdapters.indexOf(delegateAdapter);
        }
    }

    throw new RuntimeException("没有找到可以处理的委托Adapter");
}

然后是onCreateViewHolder,既然是通过委托Adapter的在集合中的index去标记的ViewType,那么在onCreateViewHolder中就非常简单了。

[java] view plain copy print?
  1. @Override  
  2. public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  3.     // 找到对应的委托Adapter  
  4.     IDelegateAdapter delegateAdapter = delegateAdapters.get(viewType);  
  5.     // 把onCreateViewHolder交给委托Adapter去处理  
  6.     RecyclerView.ViewHolder viewHolder = delegateAdapter.onCreateViewHolder(parent, viewType);  
  7.     return viewHolder;  
  8. }  
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    // 找到对应的委托Adapter
    IDelegateAdapter delegateAdapter = delegateAdapters.get(viewType);
    // 把onCreateViewHolder交给委托Adapter去处理
    RecyclerView.ViewHolder viewHolder = delegateAdapter.onCreateViewHolder(parent, viewType);
    return viewHolder;
}

接下来是onBindViewHolder,类似onCreateViewHolder,这里也是找到委托Adapter,交给他去处理。

[java] view plain copy print?
  1. @Override  
  2. public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {  
  3.     // 找到当前ViewHolder的ViewType,也就是委托Adapter在集合中的index  
  4.     int viewType = holder.getItemViewType();  
  5.     // 找到对应的委托Adapter  
  6.     IDelegateAdapter delegateAdapter = delegateAdapters.get(viewType);  
  7.     // 把onBindViewHolder交给委托Adapter去处理  
  8.     delegateAdapter.onBindViewHolder(holder, position, dataItems.get(position));  
  9. }  
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    // 找到当前ViewHolder的ViewType,也就是委托Adapter在集合中的index
    int viewType = holder.getItemViewType();
    // 找到对应的委托Adapter
    IDelegateAdapter delegateAdapter = delegateAdapters.get(viewType);
    // 把onBindViewHolder交给委托Adapter去处理
    delegateAdapter.onBindViewHolder(holder, position, dataItems.get(position));
}

使用的时候也非常简单,在之前的基础上注册委托Adapter就可以了。

[java] view plain copy print ?
  1. RecyclerView recyclerView = findViewById(R.id.recycler_view);  
  2. LinearLayoutManager layoutManager = new LinearLayoutManager(this);  
  3. recyclerView.setLayoutManager(layoutManager);  
  4. NewsAdapter mAdapter = new NewsAdapter();  
  5. // 添加委托Adapter  
  6. mAdapter.addDelegate(new OnePicDelegateAdapter());  
  7. mAdapter.addDelegate(new ThreePicDelegateAdapter());  
  8. mAdapter.addDelegate(new MorePicDelegateAdapter());  
  9.   
  10. recyclerView.setAdapter(mAdapter);  
RecyclerView recyclerView = findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
NewsAdapter mAdapter = new NewsAdapter();
// 添加委托Adapter
mAdapter.addDelegate(new OnePicDelegateAdapter());
mAdapter.addDelegate(new ThreePicDelegateAdapter());
mAdapter.addDelegate(new MorePicDelegateAdapter());

recyclerView.setAdapter(mAdapter);

双扩充样式

产品经理又来了,说又要添加一种视频类型的样式。你爽快地说:“没问题”。

之前一直是通过图片的个数来判断是那种形式,但这次明显不能这么干了,修改原来的实体,添加一个type来标识是那种类型。

[java] view plain copy print ?
  1. public class News {  
  2.     public int type = 0; // 0:一张图片;1:三张图片;2:多张图片;3:视频类型  
  3.     public List<String> imgUrls = null;  
  4.     public String content = "";  
  5.     public String count = "";  
  6.     public String duration = "";  
  7.     public String source = "";  
  8.     public String time = "";  
  9.     public String link = "";  
  10. }  
public class News {
    public int type = 0; // 0:一张图片;1:三张图片;2:多张图片;3:视频类型
    public List<String> imgUrls = null;
    public String content = "";
    public String count = "";
    public String duration = "";
    public String source = "";
    public String time = "";
    public String link = "";
}

添加xml布局

[html] view plain copy print ?
  1. <?xml version="1.0" encoding= "utf-8"?>  
  2. <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="wrap_content"  
  7.     android:paddingLeft="10dp"  
  8.     android:paddingRight="10dp"  
  9.     android:paddingTop="10dp">  
  10.   
  11.     <TextView  
  12.         android:id="@+id/tv_content"  
  13.         android:layout_width="0dp"  
  14.         android:layout_height="wrap_content"  
  15.         android:ellipsize="end"  
  16.         android:maxLines="2"  
  17.         android:textColor="#333333"  
  18.         android:textSize="18sp"  
  19.         app:layout_constraintLeft_toLeftOf="parent"  
  20.         app:layout_constraintRight_toRightOf="parent"  
  21.         app:layout_constraintTop_toTopOf="parent"  
  22.         tools:text="这是一条新闻,这是一条新闻,这是一条新闻" />  
  23.   
  24.     <ImageView  
  25.         android:id="@+id/iv_pic"  
  26.         android:layout_width="0dp"  
  27.         android:layout_height="0dp"  
  28.         android:layout_marginTop="10dp"  
  29.         android:scaleType="centerCrop"  
  30.         app:layout_constraintDimensionRatio="5:3"  
  31.         app:layout_constraintLeft_toLeftOf="parent"  
  32.         app:layout_constraintRight_toRightOf="parent"  
  33.         app:layout_constraintTop_toBottomOf="@+id/tv_content"  
  34.         tools:src="@mipmap/ic_launcher" />  
  35.   
  36.     <ImageView  
  37.         android:layout_width="48dp"  
  38.         android:layout_height="48dp"  
  39.         android:src="@mipmap/play90"  
  40.         app:layout_constraintBottom_toBottomOf="@+id/iv_pic"  
  41.         app:layout_constraintLeft_toLeftOf="@+id/iv_pic"  
  42.         app:layout_constraintRight_toRightOf="@+id/iv_pic"  
  43.         app:layout_constraintTop_toTopOf="@+id/iv_pic" />  
  44.   
  45.     <TextView  
  46.         android:id="@+id/tv_duration"  
  47.         android:layout_width="wrap_content"  
  48.         android:layout_height="wrap_content"  
  49.         android:paddingBottom="6dp"  
  50.         android:paddingRight="10dp"  
  51.         android:textColor="@android:color/white"  
  52.         app:layout_constraintBottom_toBottomOf="@+id/iv_pic"  
  53.         app:layout_constraintRight_toRightOf="@+id/iv_pic"  
  54.         tools:text="12:34" />  
  55.   
  56.     <TextView  
  57.         android:id="@+id/tv_source"  
  58.         android:layout_width="wrap_content"  
  59.         android:layout_height="wrap_content"  
  60.         android:layout_marginTop="10dp"  
  61.         android:textColor="#888888"  
  62.         android:textSize="12sp"  
  63.         app:layout_constraintLeft_toLeftOf="parent"  
  64.         app:layout_constraintTop_toBottomOf="@+id/iv_pic"  
  65.         tools:text="澎湃新闻" />  
  66.   
  67.     <TextView  
  68.         android:id="@+id/tv_time"  
  69.         android:layout_width="wrap_content"  
  70.         android:layout_height="wrap_content"  
  71.         android:layout_marginLeft="8dp"  
  72.         android:textColor="#888888"  
  73.         android:textSize="12sp"  
  74.         app:layout_constraintLeft_toRightOf="@+id/tv_source"  
  75.         app:layout_constraintTop_toTopOf="@+id/tv_source"  
  76.         tools:text="07:33" />  
  77.   
  78.     <View  
  79.         android:layout_width="0dp"  
  80.         android:layout_height="1px"  
  81.         android:layout_margin="10dp"  
  82.         android:background="#EEEEEE"  
  83.         app:layout_constraintTop_toBottomOf="@id/tv_source" />  
  84.   
  85. </android.support.constraint.ConstraintLayout>  
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="10dp">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ellipsize="end"
        android:maxLines="2"
        android:textColor="#333333"
        android:textSize="18sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="这是一条新闻,这是一条新闻,这是一条新闻" />

    <ImageView
        android:id="@+id/iv_pic"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginTop="10dp"
        android:scaleType="centerCrop"
        app:layout_constraintDimensionRatio="5:3"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_content"
        tools:src="@mipmap/ic_launcher" />

    <ImageView
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:src="@mipmap/play90"
        app:layout_constraintBottom_toBottomOf="@+id/iv_pic"
        app:layout_constraintLeft_toLeftOf="@+id/iv_pic"
        app:layout_constraintRight_toRightOf="@+id/iv_pic"
        app:layout_constraintTop_toTopOf="@+id/iv_pic" />

    <TextView
        android:id="@+id/tv_duration"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingBottom="6dp"
        android:paddingRight="10dp"
        android:textColor="@android:color/white"
        app:layout_constraintBottom_toBottomOf="@+id/iv_pic"
        app:layout_constraintRight_toRightOf="@+id/iv_pic"
        tools:text="12:34" />

    <TextView
        android:id="@+id/tv_source"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textColor="#888888"
        android:textSize="12sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/iv_pic"
        tools:text="澎湃新闻" />

    <TextView
        android:id="@+id/tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:textColor="#888888"
        android:textSize="12sp"
        app:layout_constraintLeft_toRightOf="@+id/tv_source"
        app:layout_constraintTop_toTopOf="@+id/tv_source"
        tools:text="07:33" />

    <View
        android:layout_width="0dp"
        android:layout_height="1px"
        android:layout_margin="10dp"
        android:background="#EEEEEE"
        app:layout_constraintTop_toBottomOf="@id/tv_source" />

</android.support.constraint.ConstraintLayout>

添加委托Adapter

[java] view plain copy print?
  1. public class VideoDelegateAdapter implements IDelegateAdapter {  
  2.   
  3.     @Override  
  4.     public boolean isForViewType(News news) {  
  5.         // 我能处理视频类型  
  6.         return news.type == 3;  
  7.     }  
  8.   
  9.     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  10.         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_video, parent, false);  
  11.         VideoViewHolder holder = new VideoViewHolder(view);  
  12.         return holder;  
  13.     }  
  14.   
  15.     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news) {  
  16.         VideoViewHolder viewHolder = (VideoViewHolder) holder;  
  17.         viewHolder.tvContent.setText(news.content);  
  18.         viewHolder.tvSource.setText(news.source);  
  19.         viewHolder.tvTime.setText(news.time);  
  20.         viewHolder.tvDuration.setText(news.duration);  
  21.         Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);  
  22.     }  
  23.   
  24.     static class VideoViewHolder extends RecyclerView.ViewHolder {  
  25.         ImageView ivPic;  
  26.         TextView tvContent;  
  27.         TextView tvDuration;  
  28.         TextView tvSource;  
  29.         TextView tvTime;  
  30.   
  31.         public VideoViewHolder(View view) {  
  32.             super(view);  
  33.             ivPic = view.findViewById(R.id.iv_pic);  
  34.             tvContent = view.findViewById(R.id.tv_content);  
  35.             tvDuration = view.findViewById(R.id.tv_duration);  
  36.             tvSource = view.findViewById(R.id.tv_source);  
  37.             tvTime = view.findViewById(R.id.tv_time);  
  38.         }  
  39.     }  
  40.   
  41. }  
public class VideoDelegateAdapter implements IDelegateAdapter {

    @Override
    public boolean isForViewType(News news) {
        // 我能处理视频类型
        return news.type == 3;
    }

    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_video, parent, false);
        VideoViewHolder holder = new VideoViewHolder(view);
        return holder;
    }

    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, News news) {
        VideoViewHolder viewHolder = (VideoViewHolder) holder;
        viewHolder.tvContent.setText(news.content);
        viewHolder.tvSource.setText(news.source);
        viewHolder.tvTime.setText(news.time);
        viewHolder.tvDuration.setText(news.duration);
        Glide.with(holder.itemView.getContext()).load(news.imgUrls.get(0)).into(viewHolder.ivPic);
    }

    static class VideoViewHolder extends RecyclerView.ViewHolder {
        ImageView ivPic;
        TextView tvContent;
        TextView tvDuration;
        TextView tvSource;
        TextView tvTime;

        public VideoViewHolder(View view) {
            super(view);
            ivPic = view.findViewById(R.id.iv_pic);
            tvContent = view.findViewById(R.id.tv_content);
            tvDuration = view.findViewById(R.id.tv_duration);
            tvSource = view.findViewById(R.id.tv_source);
            tvTime = view.findViewById(R.id.tv_time);
        }
    }

}

添加注册委托代理

[java] view plain copy print?
  1. // 添加委托Adapter  
  2. mAdapter.addDelegate(new OnePicDelegateAdapter());  
  3. mAdapter.addDelegate(new ThreePicDelegateAdapter());  
  4. mAdapter.addDelegate(new MorePicDelegateAdapter());  
  5. mAdapter.addDelegate(new VideoDelegateAdapter()); // 新添加的视频类型  
// 添加委托Adapter
mAdapter.addDelegate(new OnePicDelegateAdapter());
mAdapter.addDelegate(new ThreePicDelegateAdapter());
mAdapter.addDelegate(new MorePicDelegateAdapter());
mAdapter.addDelegate(new VideoDelegateAdapter()); // 新添加的视频类型

经过非常简单的三步之后,就很清爽地完成了这次需求,大家都很开心。


叒扩充样式

产品经理再一次找到你,说我们的新闻很火爆,希望加入广告。每10条加入一个广告,是不是很好做。你心想,卧槽,这他么不是坑我么。没办法,产品经理的性子你也知道,这个肯定是要做的。

这么看来之前的封装还是不能满足的,之前的是要求所有的数据都有相同的类型。那能不能加入不同数据类型的支持呢?

看下之前的是通过一个List来保存托管Adapter的,这样是无法保存不同的类型信息的。

[java] view plain copy print?
  1. List<IDelegateAdapter> delegateAdapters = new ArrayList<>();  
List<IDelegateAdapter> delegateAdapters = new ArrayList<>();

那就再增加一个保存类型信息的List吧,为了便于查找,这里使用Android提供的SparseArrayCompat。两个集合定义如下:

[java] view plain copy print?
  1. // 用于保存委托Adapter  
  2. private SparseArrayCompat<AdapterDelegate<Object>> delegates = new SparseArrayCompat();  
  3. // 用于保存委托Adapter能处理的类型  
  4. private SparseArray<String> dataTypes = new SparseArray<>();  
// 用于保存委托Adapter
private SparseArrayCompat<AdapterDelegate<Object>> delegates = new SparseArrayCompat();
// 用于保存委托Adapter能处理的类型
private SparseArray<String> dataTypes = new SparseArray<>();
[java] view plain copy print?
  1. public AdapterDelegatesManager addDelegate(AdapterDelegate<Object, VH> delegate) {  
  2.     Type superclass = delegate.getClass().getGenericSuperclass();  
  3.     if (superclass instanceof ParameterizedType) {  
  4.         Class<?> clazz = (Class<?>) ((ParameterizedType) superclass).getActualTypeArguments()[0];  
  5.         String typeWithTag = clazz.getName();  
  6.   
  7.         int viewType = delegates.size();  
  8.         // Save the delegate to the collection;  
  9.         delegates.put(viewType, delegate);  
  10.         // Save the index of the delegate to the collection;  
  11.         dataTypes.put(viewType, typeWithTag);  
  12.     } else {  
  13.         // Has no generics.  
  14.         throw new IllegalArgumentException(  
  15.                 String.format("Please set the correct generic parameters on %s.", delegate.getClass().getName()));  
  16.     }  
  17.     return this;  
  18. }  
public AdapterDelegatesManager addDelegate(AdapterDelegate<Object, VH> delegate) {
    Type superclass = delegate.getClass().getGenericSuperclass();
    if (superclass instanceof ParameterizedType) {
        Class<?> clazz = (Class<?>) ((ParameterizedType) superclass).getActualTypeArguments()[0];
        String typeWithTag = clazz.getName();

        int viewType = delegates.size();
        // Save the delegate to the collection;
        delegates.put(viewType, delegate);
        // Save the index of the delegate to the collection;
        dataTypes.put(viewType, typeWithTag);
    } else {
        // Has no generics.
        throw new IllegalArgumentException(
                String.format("Please set the correct generic parameters on %s.", delegate.getClass().getName()));
    }
    return this;
}

每向delegates中添加一个委托Adapter,则向dataTypes中添加该委托Adapter能处理的类型。比如:向delegates添加一个能处理String类型的委托 AdapterDelegate<String>位置是1,那么向dataType中添加一个java.lang.String位置也是1。那么在处理String类型的数据的时候在dataType中查找到对应的位置为1,就可以去delegate的1位置取对应的委托Adapter就可以啦。

这里就不再贴代码了,github地址DelegationAdapter

叕来需求

经过以上封装,你开心地工作了相当长的一段时间。有一天产品又找到你,我们要做一个账单详情页,很简单,就是显示他的消费信息。


你一看,上面是固定的信息,下面是固定的信息,中间是一个列表。使用ListView的添加头部、尾部会非常简单,但是你又不想使用ListView。能不能使用封装的RecyclerView委托Adapter实现呢?

[java] view plain copy print?
  1. public class Bill {  
  2.   
  3.     public String title = "";            // 标题  
  4.     public String waiter = "";           // 服务员  
  5.     public String cashier = "";          // 收银员  
  6.     public int ramadhin = 0;             // 桌号  
  7.     public int guests = 0;               // 客人数  
  8.     public String beginTime = "";       // 开台时间  
  9.     public String endTime = "";          // 结账时间  
  10.     public String duration = "";         // 用餐时长  
  11.     public List<Item> details = null;   // 用餐详情  
  12.     public String total = "";            // 合计  
  13.     public String discounts = "";       // 优惠  
  14.     public String receivable = "";      // 应收  
  15.     public String describe = "";         // 描述信息  
  16.   
  17.     public static class Item {  
  18.         public String name = "";    // 名称  
  19.         public String count = "";   // 数量  
  20.         public String price = "";   // 单价  
  21.         public String subtotal = "";// 小计  
  22.     }  
  23.   
  24. }  
public class Bill {

    public String title = "";           // 标题
    public String waiter = "";          // 服务员
    public String cashier = "";         // 收银员
    public int ramadhin = 0;            // 桌号
    public int guests = 0;              // 客人数
    public String beginTime = "";       // 开台时间
    public String endTime = "";         // 结账时间
    public String duration = "";        // 用餐时长
    public List<Item> details = null;   // 用餐详情
    public String total = "";           // 合计
    public String discounts = "";       // 优惠
    public String receivable = "";      // 应收
    public String describe = "";        // 描述信息

    public static class Item {
        public String name = "";    // 名称
        public String count = "";   // 数量
        public String price = "";   // 单价
        public String subtotal = "";// 小计
    }

}

通过分析,应该是三个委托Adapter,即上中下三部分。中间部分还好,类型是Bill.Item,但是上下两部分就无法区分了,都是Bill类型。

那怎样才能区分相同数据类型同一数据的委托Adapter呢?能不能给每个委托Adapter打一个Tag,在设置数据的时候也添加Tag,这样就可以通过Tag去区分了。修改设置数据类型标记的部分。

[java] view plain copy print?
  1. // 用于保存委托Adapter  
  2. private SparseArrayCompat<AdapterDelegate<Object, VH>> delegates = new SparseArrayCompat();  
  3. // 用于保存委托Adapter能处理的类型  
  4. private SparseArray<String> dataTypeWithTags = new SparseArray<>();  
// 用于保存委托Adapter
private SparseArrayCompat<AdapterDelegate<Object, VH>> delegates = new SparseArrayCompat();
// 用于保存委托Adapter能处理的类型
private SparseArray<String> dataTypeWithTags = new SparseArray<>();
[java] view plain copy print?
  1. public AdapterDelegatesManager addDelegate(AdapterDelegate<Object, VH> delegate, String tag) {  
  2.     Type superclass = delegate.getClass().getGenericSuperclass();  
  3.     if (superclass instanceof ParameterizedType) {  
  4.         Class<?> clazz = (Class<?>) ((ParameterizedType) superclass).getActualTypeArguments()[0];  
  5.         String typeWithTag = clazz.getName() + ":" + tag;  
  6.   
  7.         int viewType = delegates.size();  
  8.         // Save the delegate to the collection;  
  9.         delegates.put(viewType, delegate);  
  10.         // Save the index of the delegate to the collection;  
  11.         dataTypeWithTags.put(viewType, typeWithTag);  
  12.     } else {  
  13.         // Has no generics.  
  14.         throw new IllegalArgumentException(  
  15.                 String.format("Please set the correct generic parameters on %s.", delegate.getClass().getName()));  
  16.     }  
  17.     return this;  
  18. }  
public AdapterDelegatesManager addDelegate(AdapterDelegate<Object, VH> delegate, String tag) {
    Type superclass = delegate.getClass().getGenericSuperclass();
    if (superclass instanceof ParameterizedType) {
        Class<?> clazz = (Class<?>) ((ParameterizedType) superclass).getActualTypeArguments()[0];
        String typeWithTag = clazz.getName() + ":" + tag;

        int viewType = delegates.size();
        // Save the delegate to the collection;
        delegates.put(viewType, delegate);
        // Save the index of the delegate to the collection;
        dataTypeWithTags.put(viewType, typeWithTag);
    } else {
        // Has no generics.
        throw new IllegalArgumentException(
                String.format("Please set the correct generic parameters on %s.", delegate.getClass().getName()));
    }
    return this;
}

通过一个动画来总结下实现方案,开始注册了四个委托Adapter,其中A、C类型都为String,通过tag标记来区分;添加数据之后,RecyclerView进行布局,首先处理带tag为A的字符串"ABCDEF",那么到委托集合中去找对应的Adapter去进行渲染。接着一个个都完成了渲染。


OK,很轻松就搞定了。剩余代码及详细demo请移步github DelegationAdapter

以后终于能开心地撸码了。