我的Android进阶之旅强烈推荐 一种优雅的方式实现RecyclerView条目多类型

Posted 欧阳鹏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我的Android进阶之旅强烈推荐 一种优雅的方式实现RecyclerView条目多类型相关的知识,希望对你有一定的参考价值。

前言

两年之前,看到一篇文章 【一种优雅的方式实现RecyclerView条目多类型】 ,当时感觉很不错,也将这篇文章的思想运用到了自己的实际项目中。

在后续的工作中,我也给同事们推荐了此篇文章,这里转载一下这个内容在此处备份。

PS : 以下内容转载于: 【一种优雅的方式实现RecyclerView条目多类型】


转载请标明出处:
http://blog.csdn.net/xuehuayous/article/details/80021325;
本文出自:【Kevin.zhou的博客】

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

放弃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。

// 初始化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

<?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>

实体对象

public class News {
    public String imgUrl = "";
    public String content = "";
    public String source = "";
    public String time = "";
    public String link = "";
}

adapter

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

<?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的集合。

public class News {
    public List<String> imgUrls = null;
    public String content = "";
    public String source = "";
    public String time = "";
    public String link = "";
}

新样式的xml layout

<?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。

@Override
public int getItemViewType(int po

以上是关于我的Android进阶之旅强烈推荐 一种优雅的方式实现RecyclerView条目多类型的主要内容,如果未能解决你的问题,请参考以下文章

我的OpenGL学习进阶之旅解决Android OpenGL ES 调试工具 GAPID 无法识别Android设备的问题

我的OpenGL学习进阶之旅解决Android OpenGL ES 调试工具 GAPID 无法识别Android设备的问题

我的Android进阶之旅Android Studio中如何快速查找空行

我的Android进阶之旅------&gt;MIME类型大全

我的渲染技术进阶之旅推荐一个游戏引擎开发的开源电子书《游戏引擎 浅入浅出》

我的渲染技术进阶之旅推荐一个游戏引擎开发的开源电子书《游戏引擎 浅入浅出》