博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
对RecyclerView中使用Adapter中的一点思考(RecyclerView 多布局)
阅读量:6997 次
发布时间:2019-06-27

本文共 11215 字,大约阅读时间需要 37 分钟。

为何对adapter多布局的写法有思考?

对于电商app来说,一个列表页面,会有多种布局,基本上都是在5个布局以上,且业务逻辑各自不同,非常复杂,导致的结果 就是adapter代码过于长了,十分不优雅。例如:

可以看到我实际项目中的adapter代码都到了2000多行,这还是较少的一个页面,多的甚至3000多行的都有。

这么做的危害,以及必须进行重构的原因:

  • 代码不易维护,这种几千行的代码,要你去修改一个bug,相信任何一个人都会抖抖索索,怕搞出事情,改错一个地方就全错

  • 不支持代码复用。比如我们有一个tab下面要用的a布局 和 另外一tab下的b 布局在高保真上表现完全一致,此时按照老的 代码只能拷贝一份过去,很麻烦,以后要修改可能要修改两处。新的adapter我们希望 可以不费吹灰之力就可以利用老的布局 代码,让我们的新页面可以支持老的布局展示

  • ui层和接口层没有做分离,adapter里面的bean和服务器接口返回的bean 是同一个class。这样的危害就在于,服务器一旦 对接口有改动,ui层adapter代码就得大改。且一旦碰到 两个不同界面 不同接口 展示的是同一种样式的时候,拷贝代码 都无法解决了,不但拷贝代码,而且拷贝完毕以后还要修改代码为新的bean。实在是太麻烦。并且如果服务端同学比较忙 接口一开始不能及时提供的话,你的adapter代码里也会比较吃力,因为bean还没确定下来。

新的adaper希望达成什么目标?

  • adapter代码能有好的分层,不同的样式最好对应不同的class,方便修改。
  • 对于不同样式的class代码来说 要能迅速的适配到其他不同接口不同的页面中。也就是说复用性要好。

怎么解决我们想要解决的问题?

记住计算机界有一个名言是:任何计算机界中的问题都可以通过引入一个中间层来解决

我们可以看看老的adapter代码是怎么解决多布局问题的

首先判断到底是哪一种viewtype

然后通过这个值来加载不同的layout布局文件

最后再bindviewholder里面设置我们的数据(对应不同的viewholder)

实际上,这里面我们用到的主要就是两个类,一个是RecyclerView.Adapter,一个是RecyclerView.ViewHolder

那么下面我们就通过引入一个中间件的方式,来解决我们前文中提到的问题。

SmartRecylerAdapter

我们首先对viewholder做一次扩充:

package com.example.a16040657.adaptertest;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;/** * Created by 16040657 on 2018/4/2. */public abstract class SmartRecylerItem
extends RecyclerView.ViewHolder { private T data; /** * 这个构造函数其实并不会直接调用 * 是为了给另外一个真正使用的构造函数使用 * * @param itemView */ public SmartRecylerItem(View itemView) { super(itemView); } /** * 真正使用的构造函数是这个 * * @param itemLayoutId * @param parent */ public SmartRecylerItem(int itemLayoutId, ViewGroup parent) { this(LayoutInflater.from(parent.getContext()).inflate(itemLayoutId, parent, false)); } /** * 这个方法不多说了 * @param id * @return */ public View findViewById(int id) { return itemView.findViewById(id); } /** * adapter代码里 会直接调用这个bind方法 * * @param position * @param data */ public void bindData(int position, T data) { this.data = data; onSetData(position, data); } /** * 设置数据 这个方法交给子类来实现 * * @param position 位置 * @param data 数据 */ protected abstract void onSetData(int position, T data); /** * 这个方法就是在adapter里面的oncreateviewholder里面调用,交给子类实现。 * 只执行一次,通常我们可以在这里面设置一下view的尺寸,以及复杂嵌套布局的一些属性等等 * * @param context */ protected abstract void configView(Context context); /** * 这个方法我们用来重写findviewbyid 之类的方法 */ protected abstract void findViews();}复制代码

然后我们对adapter做一次扩充:

package com.example.a16040657.adaptertest;import android.support.v7.widget.RecyclerView;import android.util.SparseArray;import android.view.ViewGroup;import java.util.ArrayList;import java.util.List;/** * Created by 16040657 on 2018/4/2. */public class SmartRecylerAdapter extends RecyclerView.Adapter {    public SmartRecylerAdapter(List mDataList) {        this.mDataList = mDataList;    }    //存放所有数据的list    private List mDataList;    /**     * 对于多种布局来说,itemType的值都是从0开始,布局越多 这个值越大 默认为0     */    private int itemTypeIndex = 0;    /**     * 这个值就是用来保存中间件的,你要适配那种布局 就增加一个中间件到这个list中。     */    private ArrayList
smartRecylerItemMiddlewareArrayList; /** * 实际上是hashmap,key 是 itemType的值,value 就是我们的中间件。 * 用SparseArray是为了性能考虑,key 为int值的话 SparseArray 比hashmap效率要高 */ private SparseArray
smartMiddleHashMap; @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //如果不使用中间件的话,这里熟悉的代码应该是根据不同的viewtype的值 //来返回不同的layout布局 //这里我们就直接根据viewtype的值 来取出我们对应的中间件 Object item = smartMiddleHashMap.get(viewType); // Item if (item instanceof SmartRecylerItemMiddleware) { SmartRecylerItemMiddleware itemFactory = (SmartRecylerItemMiddleware) item; return itemFactory.dispatchCreatesSmartItem(parent); } throw new IllegalStateException("这边异常随便你自己怎么写吧。"); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof SmartRecylerItem) { ((SmartRecylerItem) holder).bindData(position, getItem(position)); } } public void addMiddleWare(SmartRecylerItemMiddleware smartRecylerItemMiddleware) { smartRecylerItemMiddleware.setItemType(itemTypeIndex++); if (smartMiddleHashMap == null) { smartMiddleHashMap = new SparseArray(); } smartMiddleHashMap.put(smartRecylerItemMiddleware.getItemType(), smartRecylerItemMiddleware); if (smartRecylerItemMiddlewareArrayList == null) { smartRecylerItemMiddlewareArrayList = new ArrayList
(5); } smartRecylerItemMiddlewareArrayList.add(smartRecylerItemMiddleware); } @Override public int getItemCount() { return mDataList.size(); } @Override public int getItemViewType(int position) { //遍历一下中间件 看看是否有对应的中间件可以使用,如果没有就抛出异常 SmartRecylerItemMiddleware smartRecylerItemMiddleware; for (int w = 0, size = smartRecylerItemMiddlewareArrayList.size(); w < size; w++) { smartRecylerItemMiddleware = smartRecylerItemMiddlewareArrayList.get(w); if (smartRecylerItemMiddleware.isTarget(mDataList.get(position))) { return smartRecylerItemMiddleware.getItemType(); } } //你当然可以在异常信息里写出更详细的信息。。。。 throw new IllegalStateException("Didn't find suitable Middleware."); } /** * 返回list中的bean * * @param position * @return */ public Object getItem(int position) { // 数据 return mDataList.get(position); }}复制代码

最后看下我们的中间件代码:

package com.example.a16040657.adaptertest;import android.view.ViewGroup;/** * 中间件,这个类的主要作用就是桥接adapter和viewholder使用 */public abstract class SmartRecylerItemMiddleware
{ /** * 这个值就是在多布局的情况下,返回的itemType的值,我们在中间件里储存一下这个值 * 方便使用 */ private int itemType; /** * 匹配数据 * * @param data 待匹配的数据,通常是使用instanceof关键字匹配类型 * @return 如果返回true,Adapter将会使用此ItemFactory来处理当前这条数据 */ public abstract boolean isTarget(Object data); /** * 创建Item */ public abstract T createSmartItem(ViewGroup parent); public int getItemType() { return itemType; } public void setItemType(int itemType) { this.itemType = itemType; } protected T dispatchCreatesSmartItem(ViewGroup parent) { T item = createSmartItem(parent); item.findViews(); item.configView(parent.getContext()); return item; }}复制代码

注释已经写的很详细了,其实就是在adapter和viewholder里面架起了一座桥梁,对使用者来说不用再关心oncrateviewholder

onbind 以及getItemviewtype这种方法了,只用专心写业务逻辑就好。

看看使用效果:

我们假设列表页就只支持两种布局效果(毕竟是demo不搞的太复杂了) ,那就是要写2个中间件

package com.example.a16040657.adaptertest;import android.content.Context;import android.view.ViewGroup;import android.widget.TextView;/** * Created by 16040657 on 2018/3/23. */public class StudentItemMiddleWare extends SmartRecylerItemMiddleware
{ @Override public boolean isTarget(Object data) { return data instanceof Student; } @Override public StudentItem createSmartItem(ViewGroup parent) { return new StudentItem(R.layout.student_item_layout, parent); } public class StudentItem extends SmartRecylerItem
{ TextView nameTv; TextView name2Tv; public StudentItem(int itemLayoutId, ViewGroup parent) { super(itemLayoutId, parent); } @Override protected void onSetData(int position, Student student) { nameTv.setText(student.name); name2Tv.setText(student.age); } @Override protected void configView(Context context) { } @Override protected void findViews() { nameTv = (TextView) findViewById(R.id.tv); name2Tv = (TextView) findViewById(R.id.tv2); } }}复制代码
package com.example.a16040657.adaptertest;import android.content.Context;import android.view.ViewGroup;import android.widget.TextView;/** * Created by 16040657 on 2018/3/23. */public class UserItemFactoryMiddleWare extends SmartRecylerItemMiddleware
{ @Override public boolean isTarget(Object data) { return data instanceof User; } @Override public UserItem createSmartItem(ViewGroup parent) { return new UserItem(R.layout.user_item_layout, parent); } public class UserItem extends SmartRecylerItem
{ TextView nameTv; TextView name2Tv; public UserItem(int itemLayoutId, ViewGroup parent) { super(itemLayoutId, parent); } @Override protected void onSetData(int position, User user) { nameTv.setText(user.name); name2Tv.setText(user.age); } @Override protected void configView(Context context) { } @Override protected void findViews() { nameTv = (TextView) findViewById(R.id.tv); name2Tv = (TextView) findViewById(R.id.tv2); } }}复制代码
recyclerView = (RecyclerView) findViewById(R.id.rv);        recyclerView.setLayoutManager(new LinearLayoutManager(this));        List dataList = new ArrayList<>();        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        dataList.add(new User("wuyueu", "wuyueu2"));        dataList.add(new Student("wuyueus", "wuyues2"));        SmartRecylerAdapter smartRecylerAdapter=new SmartRecylerAdapter(dataList);        //这边代码很好理解吧,以后其他页面如果也要用这边的布局代码 直接add 这个我们的中间件就可以了        //其他的都不需要关心,反正我们的adapter bean是object。其他页面只要自己把服务器返回的bean        //转成我们这里的bean即可。且不同的布局对应着不同的中间件,即使是其他组的人来修改你的代码        //也不怕埋坑啦。逻辑十分清晰。        smartRecylerAdapter.addMiddleWare(new StudentItemMiddleWare());        smartRecylerAdapter.addMiddleWare(new UserItemFactoryMiddleWare());        recyclerView.setAdapter(smartRecylerAdapter);复制代码

最后看一下效果(明显看出来是加载了不同的布局哈):

感想

很多同学都觉得写业务代码提高不了技术能力,其实不是的,当你觉得你的业务代码各种别扭,各种需要复制粘贴, 各种改一点需求都要很多工作量的时候,就是时候重构你的代码了,adapter只是一个小点。其实还是有很多业务代码 值得我们持续修改优化的,再牛逼的架构也要在生产中实践,所以何不从业务代码中磨练你的代码能力呢?

转载地址:http://jyzvl.baihongyu.com/

你可能感兴趣的文章
jqgrid 加入右键菜单按钮管理
查看>>
Redis集群的主从切换研究
查看>>
养成良好的编程习惯
查看>>
编译hadoop的libhdfs.a
查看>>
PHP简易计算器方法2
查看>>
意见整理
查看>>
Linux安装Tomcat,运行Eclipse,web项目
查看>>
计算机网络笔记
查看>>
mysql 查重复数据
查看>>
【c学习-10】
查看>>
GNU make 总结 (四)
查看>>
poj1611(并查集简单应用)
查看>>
python 文件读写模式r,r+,w,w+,a,a+的区别(附代码示例)
查看>>
asp.net web 通过IHttpAsyncHandler接口进行消息推送
查看>>
wcf 使用sqlMembership证书认证
查看>>
MogoDB安装与使用(Windows篇)
查看>>
Objective-C Runtime 运行时之一:类与对象
查看>>
[Python]打开文件的模式
查看>>
UVALive5429 UVA382 POJ1528 HDU1323 ZOJ1284 Perfection
查看>>
HDU1195 ZOJ2416 Open the Lock【BFS】
查看>>