内容简介: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) 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Black Box Society
Frank Pasquale / Harvard University Press / 2015-1-5 / USD 35.00
Every day, corporations are connecting the dots about our personal behavior—silently scrutinizing clues left behind by our work habits and Internet use. The data compiled and portraits created are inc......一起来看看 《The Black Box Society》 这本书的介绍吧!