内容简介: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作为实验性功能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
计算机组成与设计硬件/软件接口
[美] David A.Patterson、John L.Hennessy / 郑纬民 / 机械工业出版社 / 2007-4 / 75.00元
《计算机组成与设计硬件:软件接口》(原书第3版)是计算机组成的经典教材。全书着眼于当前计算机设计中最基本的概念,展示了软硬件间的关系,并全面介绍当代计算机系统发展的主流技术和最新成就。同以往版本一样,《计算机组成与设计硬件:软件接口》(原书第3版)采用MIPS处理器作为展示计算机硬件技术基本功能的核心。书中逐条指令地列举了完整的MIPS指令集,并介绍了网络和多处理器结构的基本内容。将CPU性能和程......一起来看看 《计算机组成与设计硬件/软件接口》 这本书的介绍吧!