<Android 基础(三十五)> RecyclerView多类型Item的正确实现姿势
简介
RecyclerView是我们开发过程中经常使用到的一个元素,原生的RecyclerView.Adapter基本上可以满足一般的需求,关于RecyclerView的基础介绍请移步:
关于多类型的Item,原生的Adapter可以通过getItemViewType返回对应的ViewHolder类型,然后在onCreateViewHolder传入的type参数,生成不同的ViewHolder,更要命的是数据绑定过程中onBindViewHolder只传入ViewHolder对象和position,针对不同的ViewHolder,我们只能通过instanceof和强制类型装换来对不同的ViewHolder进行不同的数据操作。
这样做的弊端就是,后续如果需要添加一个类型,几乎需要修改Adapter类中的每一个方法,不利于维护。
类结构
目的
- 便于维护。增删Item的类型不需要修改Adapter的代码;
- 条例清晰。不同类型的Item对应不同的Bean类,对应不用的ViewHolder,对应不同的layout
item type <—-> layout <—> ViewHolder <—-> java bean - 工厂接口。抽象出TypeFactory接口,针对同一个Bean,可以使用不同的layout和不同的ViewHolder。
思路
- 所有Bean类实现统一Visitable接口,实现type方法,返回对应的layout id;
- 创建BaseViewHolder使用泛型,T为Bean类。使用SparseArray存储View,View的id作为唯一标识。
- 具体的ViewHolder类,继承BaseViewHolder,传入不同的Bean类,方便不同的ViewHolder中绑定不同的数据。
- Adapter实现中使用Visitable List同一处理列表数据,使用事项TypeFactory接口的实例返回ItemType,viewHolder等
代码结构
代码内容
Visitable接口
public interface Visitable {
int type(TypeFactory typeFactory);
}
TypeFactory接口
public interface TypeFactory {
int type(BannerBean bannerBean);
int type(ContentBean contentBean);
int type(SearchBean searchBean);
int type(DividerBean dividerBean);
BaseViewHolder createViewHolder(int type, View itemView);
}
BaseViewHolder抽象类
public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder{
SparseArray<View> mViews;
View mItemView;
public BaseViewHolder(View itemView) {
super(itemView);
mItemView = itemView;
mViews = new SparseArray<>();
}
public View getView(int resId) {
View view = mViews.get(resId);
if(view== null) {
view = mItemView.findViewById(resId);
mViews.put(resId, view);
}
return view;
}
public abstract void bindViewData(T data);
}
TypeFactory实现类~ItemTypeFactory
public class ItemTypeFactory implements TypeFactory {
public static final int BANNER_ITEM_LAYOUT = R.layout.rv_item_banner;
public static final int CONTENT_ITEM_LAYOUT = R.layout.rv_item_content;
public static final int SEARCH_ITEM_LAYOUT = R.layout.rv_item_search;
public static final int DIVIDER_ITEM_LAYOUT = R.layout.rv_item_divider;
@Override
public int type(BannerBean bannerBean) {
return BANNER_ITEM_LAYOUT;
}
@Override
public int type(ContentBean contentBean) {
return CONTENT_ITEM_LAYOUT;
}
@Override
public int type(DividerBean dividerBean){
return DIVIDER_ITEM_LAYOUT;
}
@Override
public int type(SearchBean dividerBean){
return SEARCH_ITEM_LAYOUT;
}
@Override
public BaseViewHolder createViewHolder(int type, View itemView) {
switch (type) {
case BANNER_ITEM_LAYOUT:
return new BannerViewHolder(itemView);
case SEARCH_ITEM_LAYOUT:
return new SearchViewHolder(itemView);
case CONTENT_ITEM_LAYOUT:
return new ContentViewHolder(itemView);
case DIVIDER_ITEM_LAYOUT:
return new DividerViewHolder(itemView);
default:
return null;
}
}
}
各种Java Bean类
BannerBean
public class BannerBean implements Visitable {
int[] mResIds;
public BannerBean(int[] mResIds) {
this.mResIds = mResIds;
}
public int[] getmResIds() {
return mResIds;
}
public void setmResIds(int[] mResIds) {
this.mResIds = mResIds;
}
@Override
public int type(TypeFactory typeFactory) {
return typeFactory.type(this);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="200dp">
<cn.bingoogolapple.bgabanner.BGABanner
android:id="@+id/bgabanner_header"
android:layout_width="match_parent"
android:layout_height="200dp">
</cn.bingoogolapple.bgabanner.BGABanner>
</LinearLayout>
ContentBean
public class ContentBean implements Visitable{
String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public ContentBean(String content) {
this.content = content;
}
@Override
public int type(TypeFactory typeFactory) {
return typeFactory.type(this);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_content"
android:textStyle="italic"
android:fontFamily="sans-serif-condensed"
android:textSize="16sp"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
DividerBean
public class DividerBean implements Visitable{
@Override
public int type(TypeFactory typeFactory) {
return typeFactory.type(this);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/divider">
</LinearLayout>
SearchBean
public class SearchBean implements Visitable {
@Override
public int type(TypeFactory typeFactory) {
return typeFactory.type(this);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="50dp"
android:padding="6dp">
<EditText
android:layout_marginLeft="30dp"
android:id="@+id/et_search"
android:layout_width="0dp"
android:layout_weight="9"
android:maxLines="1"
android:textSize="10sp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:hint="输入文字搜索..."
android:background="@drawable/bg_search"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/bt_search"
android:layout_width="0dp"
android:layout_weight="2"
android:src="@drawable/search"
android:layout_height="match_parent" />
</LinearLayout>
MultiRecyclerAdapter
public class MultiRecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder> {
List<Visitable> mData;
TypeFactory typeFactory;
public MultiRecyclerAdapter(List<Visitable> mData) {
this.mData = mData;
this.typeFactory = new ItemTypeFactory();
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
return typeFactory.createViewHolder(viewType, view);
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
holder.bindViewData(mData.get(position));
}
@Override
public int getItemViewType(int position) {
return mData.get(position).type(typeFactory);
}
@Override
public int getItemCount() {
return (mData != null ? mData.size() : 0);
}
}
示例
public class MultiActivity extends AppCompatActivity {
@BindView(R.id.rv_content)
RecyclerView rvContent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather);
ButterKnife.bind(this);
List<Visitable> beans = new ArrayList<>();
int[] resIds = {R.mipmap.banner_one, R.mipmap.banner_two, R.mipmap.banner_three, R.mipmap.banner_four, R.mipmap.banner_five};
beans.add(new BannerBean(resIds));
beans.add(new SearchBean());
beans.add(new DividerBean());
beans.add(new ContentBean("one"));
beans.add(new ContentBean("one"));
beans.add(new ContentBean("one"));
beans.add(new DividerBean());
beans.add(new ContentBean("two"));
beans.add(new ContentBean("two"));
beans.add(new ContentBean("two"));
beans.add(new DividerBean());
beans.add(new ContentBean("three"));
beans.add(new ContentBean("three"));
beans.add(new ContentBean("three"));
beans.add(new DividerBean());
beans.add(new ContentBean("four"));
beans.add(new ContentBean("four"));
beans.add(new ContentBean("four"));
beans.add(new DividerBean());
MultiRecyclerAdapter multiRecyclerAdapter = new MultiRecyclerAdapter(beans);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
rvContent.setAdapter(multiRecyclerAdapter);
rvContent.setLayoutManager(linearLayoutManager);
}
}
简单效果
写在最后
思路来源:
https://medium.com/@dpreussler/writing-better-adapters-1b09758407d2
github上也有很多开源的万能Adapter,但是个人感觉这个思路虽然实现上代码会相对较多,但是层次分析,便于维护,扩展性较好。想要添加一个新类型的ItemType,只需要稍作修改即可快速完成,不用修改Adapter的代码。
<Android 基础(三十五)> RecyclerView多类型Item的正确实现姿势的更多相关文章
- <Android 基础(十五)> Alert Dialog
介绍 The AlertDialog class allows you to build a variety of dialog designs and is often the only dialo ...
- Bootstrap <基础三十二>模态框(Modal)插件
模态框(Modal)是覆盖在父窗体上的子窗体.通常,目的是显示来自一个单独的源的内容,可以在不离开父窗体的情况下有一些互动.子窗体可提供信息.交互等. 如果您想要单独引用该插件的功能,那么您需要引用 ...
- Bootstrap <基础三十>Well
Well 是一种会引起内容凹陷显示或插图效果的容器 <div>.为了创建 Well,只需要简单地把内容放在带有 class .well 的 <div> 中即可.下面的实例演示了 ...
- Bootstrap <基础二十五>警告(Alerts)
警告(Alerts)以及 Bootstrap 所提供的用于警告的 class.警告(Alerts)向用户提供了一种定义消息样式的方式.它们为典型的用户操作提供了上下文信息反馈. 您可以为警告框添加一个 ...
- JAVA之旅(三十五)——完结篇,终于把JAVA写完了,真感概呐!
JAVA之旅(三十五)--完结篇,终于把JAVA写完了,真感概呐! 这篇博文只是用来水经验的,写这个系列是因为我自己的java本身也不是特别好,所以重温了一下,但是手比较痒于是就写出了这三十多篇博客了 ...
- Gradle 1.12用户指南翻译——第三十五章. Sonar 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- Java15-java语法基础(十五)——内部类
java16-java语法基础(十五)内部类 一.内部类: 可以在一个类的内部定义另一个类,这种类称为内部类. 二.内部类分为两种类型: 1.静态内部类: 静态内部类是一个具有static修饰词的类, ...
- “全栈2019”Java多线程第三十五章:如何获取线程被等待的时间?
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件、目录属性 shell数组简单用法 $( ) 和${ } 和$(( )) 与 sh -n sh -x sh -v 第三十五节课
centos shell脚本编程1 正则 shell脚本结构 read命令 date命令的用法 shell中的逻辑判断 if 判断文件.目录属性 shell数组简单用法 $( ) 和$ ...
随机推荐
- git小乌龟工具TortoiseGit记住你的账号密码
在使用TortoiseGit的过程中,发下每次push或者pull都要重复输入账号密码,非常麻烦 怎么设置记住密码 在[系统盘]:\Users[你的用户名](比如C:\User\Administrat ...
- 线程&线程控制
线程基本概念: 1 线程 (1)概念:linux下没有真正的线程,所谓的线程都是通过进程的pcb模拟的,因此linux下的线程也称为“轻量级进程”,之前我们所说的进程现在看来,可以理解为:只有一个线程 ...
- vue教程3-02 vue动画
vue教程3-02 vue动画 以下代码,已经用包管理器下载好vue,animate <!DOCTYPE html> <html lang="en"> &l ...
- list、vector、deque互相拷贝
#include <iostream> #include <stdlib.h> #include <string.h> #include <algorithm ...
- 【Canal源码分析】TableMetaTSDB
这是Canal在新版本引入的一个内容,主要是为了解决由于历史的DDL导致表结构与现有表结构不一致,导致的同步失败的问题.采用的是Druid和Fastsql,来记录表结构到DB中,如果需要进行回滚时,得 ...
- ilspy反编译
资料: http://www.cnblogs.com/JamesLi2015/archive/2011/09/08/2170519.html 软件: http://ilspy.net/
- [原创]EF架构随心所欲打造属于你自己的DbModel
前言 我们都知道EF可以生成Dbmodel,系统生成的Model有时候并不是我们想要的,如何我们要生成自己的Model,那么久需要我们手动的去修改T4模版,T4是对“Text Template Tra ...
- 面试题----makefile文件的作用
make工具和makefile文件 make工具和makefile文件简介 make命令和makefile文件的结合提供了一个在项目管理领域十分强大的工具.它不仅常被用于控制源代码的编译和链接,而且还 ...
- Tomcat学习总结(3)——Tomcat优化详细教程
Tomcat是我们经常使用的 servlet容器之一,甚至很多线上产品都使用 Tomcat充当服务器.而且优化后的Tomcat性能提升显著,本文从以下几方面进行分析优化. 一.内存优化 默认情况下To ...
- HTML 框架 frameset,frame
通过使用框架,你可以在同一个浏览器窗口中显示不止一个页面.每份HTML文档称为一个框架,并且每个框架都独立于其他的框架 框架结构标签(<frameset>) 框架结构标签(<fram ...