内容简介:RecyclerView作为Google替代ListView的一个组件,其强大的拓展性和性能,现在已经成为无数App核心页面的主体框架。RecyclerView的开发模式一般来说都是多Type类型的ViewHolder——后面就称为楼层(感觉很形象)。但是使用多了,许多问题就暴露出来了,经常考虑有这么几个问题:欢迎Star:clap:~
RecyclerView作为Google替代ListView的一个组件,其强大的拓展性和性能,现在已经成为无数App核心页面的主体框架。RecyclerView的开发模式一般来说都是多Type类型的ViewHolder——后面就称为楼层(感觉很形象)。但是使用多了,许多问题就暴露出来了,经常考虑有这么几个问题:
-
- 如何更便捷的使用Adapter和ViewHolder的开发模式?
-
- 如何和他人的楼层做到楼层的复用?
-
- 如何做到全局楼层的打通?
-
- 楼层本身如何做到逻辑闭合,做到MVP的组件化模式?
功能特性
- 基于编译期注解,不影响性能
- 使用简单,楼层耦合度低
- 代码侵入性低
- 支持全局楼层打通,多人楼层打通
- 楼层支持点对点MVP模式
- 事件中心模式,楼层只是事件的传递者。
- 生命周期监听,支持逻辑的生命周期感知。
- 丰富的API,支持多方面拓展。
- 提供组件化工程使用方案
项目地址
欢迎Star:clap:~
欢迎提issue讨论~
使用方式
这里就介绍一下基于自己对于RecyclerView的理解,开发的一款基于AOP的,适用于多楼层模式的RecyclerView的开发框架。
一.单样式列表
1.定义楼层(支持三种模式)
- 继承Component类型
@ComponentType( value = ComponentId.SIMPLE, layout = R.layout.single_text ) public class SimpleVH extends Component { public SimpleVH(Context context, View itemView) { super(context, itemView); } @Override public void onBind(int pos, Object item) { } @Override public void onUnBind() { } } 复制代码
- 继承原生ViewHolder类型
@ComponentType( value = PersonId.VIEWHOLDER, layout = R.layout.person_item_layout ) public class PersonVH extends RecyclerView.ViewHolder implements IComponentBind<PersonModel> { private TextView tvName; public PersonVH(View itemView) { super(itemView); tvName = itemView.findViewById(R.id.tv_name); } @Override public void onBind(int pos, PersonModel item) { tvName.setText(item.name); } @Override public void onUnBind() { } } 复制代码
- 自定义View类型
@ComponentType(PersonId.CUSTOM) public class CustomView extends LinearLayout implements IComponentBind<PersonModel> { public CustomView(Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.cutom_view_vh, this, true); setBackgroundColor(Color.BLACK); } @Override public void onBind(int pos, PersonModel item) { } @Override public void onUnBind() { } } 复制代码
很清晰,不用再每次在复杂的 if else
中寻找自己楼层对应的布局文件。(熟悉的人应该都懂)
注意:
- value:楼层的唯一标示,int型
- layout:楼层的布局文件
-
继承ViewHolder和自定义View类型需要实现
IComponentBind
接口即可 - 对于R文件不是常量在组件化时遇到的问题的 解决方案
2.定义Model
@BindType(ComponentId.SIMPLE) public class SimpleModel { } 复制代码
BindType:当是单样式时,model直接注解对应的楼层的唯一标示,int型
3.绑定RecyclerView
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.common_layout); mRcy = findViewById(R.id.rcy); mRcy.setLayoutManager(new LinearLayoutManager(this)); new ToolKitBuilder<>(this, mData).build().bind(mRcy); } 复制代码
使用对应的API,利用build()方法构建SlotsContext实体最后利用 bind()
方法绑定ReyclerView.
二.多楼层模式
1.定义ViewHolder(同前一步)
2.多样式判断逻辑(两种方式)
2.1 Model实现HandlerType接口处理逻辑
public class CommonModel implements HandlerType { public int pos; public String tips; public String eventId; @Override public int handlerType() { if (pos > 8) { pos = pos % 8; } switch (pos) { case 1: return ComponentId.VRCY; case 3: return ComponentId.DIVIDER; case 4: return ComponentId.WEBVIEW; case 5: return ComponentId.TEXT_IMG; case 6: return ComponentId.IMAGE_TWO_VH; case 7: return ComponentId.IMAGE_VH; case 8: return ComponentId.USER_INFO_LAYOUT; } return ComponentId.VRCY; } } 复制代码
返回定义的ItemViewType,这里封装在Model内部,是由于平时我们总是将 java 中的Model当作一个JavaBean,而导致我们赋予Model的职责过于轻,所以就会出现更多的其实和Model紧密相关的逻辑放到了Activity,Presenter或者别的地方,但是其实当我们将Model当作数据层来看待,其实可以将许多与Model紧密相关的逻辑放到Model中,这样我们其实单模块的逻辑内聚度就很高,便于我们理解。 (这里思路其实来源于IOS开发中的 胖Model 的概念,大家可以Goolge一下)
好处:当我们需要确定楼层之间和Model的关系,直接按住ctrl,进入Model类,一下就可以找到相关逻辑。
2.2 实现IModerBinder接口自定义处理类
一款好的框架肯定是对修改关闭,对拓展开放的,当我们认为放到Model中处理过于粗暴,或者Model中已经有过多的逻辑了,我们也可以将逻辑抽出来,实现IModerBinder接口。
public interface IModerBinder<T> { int getItemType(int pos, T t); } 复制代码
对应的利用 ToolKitBuilder.setModerBinder(IModerBinder<T> moderBinder)
构建即可。例如:
.setModerBinder(new ModelBinder<PersonModel>() { @Override protected int bindItemType(int pos, PersonModel obj) { //处理Type的相关逻辑 return type; } }) 复制代码
个人模式
当涉及到大型项目时,多人协作往往是一个问题,当所有人都维护一套ComponentId,合并代码时解决冲突往往是很大的问题,并且不可能所有的楼层都是全局打通的类型,所以这里提供一种个人开发模式。
用法
- 1.使用attach注解,绑定对应class
@ComponentType( value = PersonId.VIEWHOLDER, layout = R.layout.person_item_layout, //class类型,对应到映射表的key attach = PersonModel.class ) public class PersonVH extends RecyclerView.ViewHolder implements IComponentBind<PersonModel> { private TextView tvName; public PersonVH(View itemView) { super(itemView); tvName = itemView.findViewById(R.id.tv_name); } @Override public void onBind(int pos, PersonModel item) { //tvName.findViewById(R.id.tv_name); tvName.setText(item.name); } @Override public void onUnBind() { } } 复制代码
- 2.调用SlotContext.attachRule绑定对应的Class
SlotContext slotContext = new ToolKitBuilder<PersonModel>(this) //注册绑定的类型,对应获取映射表 .attachRule(PersonModel.class).build(); 复制代码
进阶使用
项目利用Build模式构建SlotContext实体,SlotContext原理基于Android中的Context思想,作为一个全局代理的上下文对象,通过SlotContext,我们可以获取对应的类,进而实现对应类的获取和通信。
避免反射创建
框架本身利用反射进行创建,内部利用 LruCache
对反射对构造器进行缓存,优化反射性能。如果想要避免反射对创建,也是可以自定义创建过程。
@ComponentType( value = PersonId.INNER, view = TextView.class, //注解不使用反射 autoCreate = false ) public static class InnerVH extends RecyclerView.ViewHolder implements IComponentBind<PersonModel> { .... } 复制代码
可以将不需要反射创建对ViewHolder的 autoCreate=false
,然后通过 ToolKitBuilder. setComponentFactory()
自定义创建过程。
具体方式-> Wiki
事件中心
事件中心其实本质就是一个继承于 View.OnClickListener
的类, 所有和ViewHolder本身无关的事件
,统一传递给事件中心,再由事件中心处理,对应于一条准则:
ViewHolder只是一个专注于展示UI的壳,只做事件的传递者,不做事件的处理者。
使用方式:
@ComponentType( value = ComponetId.SINGLE_TEXT, layout = R.layout.single_text ) public class TextVH extends Component<Text> implements InjectCallback { private TextView tv; private View.OnClickListener onClickListener; public TextVH(Context context, View itemView) { super(context, itemView); tv = (TextView) itemView; } @Override public void onBind(int pos, Text item) { tv.setText(item.title); //此处所有的数据和事件类型通过setTag传出 tv.setTag(item.eventId); tv.setOnClickListener(onClickListener); } @Override public void onUnBind() { } @Override public void injectCallback(View.OnClickListener onClickListener) { this.onClickListener = onClickListener; } } 复制代码
仿照 依赖注入 的思想,只不过代码侵入性没有那么强,当然只能在onBind的时候才能绑定,构造函数的时候,事件中心对象还没有注入进来。
-
- ViewHolder实现InjectCallback接口,在onBind生命周期就可以拿到事件中心对象。
-
- 通过View.setTag,将事件类型(int型等,唯一性)和相关需要的数据传出。
事件中心的思想就是:ViewHolder单纯的只传递事件,完全由数据驱动事件,View不感知事件类型,也就是说,这个ViewHolder的事件是 可变的 !
MVP的拆分
关于MVP是什么这里就不多讲了,这里讲一讲MVP的拆分,常规的MVP我们经常做的就是一个P完成所有的逻辑,但是这时带来的问题就时P层过于大,这时我的理解就是对P进行拆分,具体拆分的粒度要根据不同的业务场景来区分(这个就比较考验开发者对于 设计模式 的理解)。而ViewHolder自身可以完成一套MVP体系,想一想,当一个特殊的楼层,涉及复杂的业务逻辑,这时完全将这个楼层拆分成MVP模式,这时其他页面需要使用的时候,只需要new对应的MVP即可。
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... slotContext = new ToolKitBuilder<>(this, mData).build(); //1.注册对应的逻辑类 slotContext.registerLogic(new CommonLogic(slotContext)); ... } @ComponentType(value = ComponentId.TEXT_IMG) //2.注解对应的逻辑类 @ILogic(CommonLogic.class) //3.实现IPresenterBind接口 public class TextImgLayout extends LinearLayout implements IComponentBind<CommonModel>,IPresenterBind<CommonLogic> { private View root; private TextView tvInfo; private CommonLogic logic; ... @Override public void onBind(int pos, CommonModel item) { tvInfo.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (logic != null) { //对应的P,处理业务逻辑 logic.pageTransfer(); } } }); } ... @Override public void injectPresenter(CommonLogic commonLogic) { this.logic = commonLogic; } } 复制代码
对应的需要三步:
-
-
slotContext.registerLogic(IPresenter presener)
,这里IPresenter只是一个空接口,用于表明这是一个逻辑层的类。
-
-
- 在ViewHolder利用@ILogic注解对应的P的Class
-
- ViewHolder实现IPresenterBind接口,注入注册给SlotContext对应的Presenter.
生命周期感知
无论是Presenter还是任何其他类,当脱离的Activity,对于生命周期的感知时非常重要的,所以SlotContext提供的有两个API
pushLife(ILifeCycle lifeCycle) pushGC(IGC gc) 复制代码
需要感知生命周期,或者仅仅感知OnDestroy的类,只需实现相应的接口,并利用api注册观察者即可。
MIX模式,多楼层打通
对于多楼层打通,我们需要利用ToolKitBuilder实现IMixStrategy策略。
public interface IMixStrategy<T> { //通过type得到真正的映射表中的ComponentId int getComponentId(int type); //通过Type确定对应的映射表 Class<?> attachClass(int type); //传入ViewHolder的Bind中的实体类 Object getBindItem(int pos, T t); } 复制代码
具体方案-> Wiki
ToolKitBuilder的构造函数
public ToolKitBuilder(Context context, List<T> data) public ToolKitBuilder(Context context) 复制代码
ToolKitBuilder的API
方法名 | 描述 | 备注 |
---|---|---|
setData(List data) | 设置绑定的数据集 | 空对象,对应的构造的size=0 |
setModerBinder(IModerBinder moderBinder) | 处理多样式时Model对应的Type | 处理优先级优先于HandlerType和注解BindType |
setEventCenter(View.OnClickListener onClickListener) | 设置事件中心 | ViewHolder的事件绑定后都会回调到这个事件中心 |
setComponentFactory(CustomFactory componentFactory) | 设置自定义创建ViewHolder的工厂 | 可以自定义创建三种类型 |
setMixStrategy(IMixStrategy mixStrategy) | 设置混合模式处理策略 | 多人楼层打通 |
attachRule(Class<?> clazz) | 注册楼层映射表 | 个人模式和混合模式 |
SlotContext build() | 构建出SlotContext对象 |
SlotContext的构造函数
public SlotContext(Context context, List<T> data) public SlotContext(ToolKitBuilder<T> builder) 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- GitLab 11.11 发布,增强协作性功能以及 DevOps 功能
- GitLab 11.11 发布,增强协作性功能以及 DevOps 功能
- 重大事件:PHP JIT 已进入 PHP RFC,并将在PHP 8.0中实现,而PHP 7.4中也将包含JIT作为实验性功能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
程序员的职业素养
Robert C.Martin / 章显洲、余晟 / 人民邮电出版社 / 2012-9-1 / 49.00元
本书是编程大师Bob 大叔40 余年编程生涯的心得体会, 讲解成为真正专业的程序员需要什么样的态度、原则,需要采取什么样的行动。作者以自己以及身边的同事走过的弯路、犯过的错误为例,意在为后来人引路,助其职业生涯迈上更高台阶。 本书适合所有程序员,也可供所有想成为具备职业素养的职场人士参考。一起来看看 《程序员的职业素养》 这本书的介绍吧!