你是否还在为项目中众多的列表样式而发愁?你是否还在为一遍遍机械重复的编写Adapter代码而厌倦?你是否还在为编写多类型卡片列表的众多细节而一次次的百度?是时候找一个更高效的手段来解脱自己了。NoAdapter——一个为解决Android列表开发的组件库。
正如这个库的名称一样,NoAdapter致力于打造一个极简的RecycleView的Adapter组件,可以让开发者更专注于业务本身的卡片(item)开发,忽略复杂、繁琐、重复(尤其是一个列表有多种卡片类型的情况)的Adapter代码的开发,不用开发一行Adapter代码,真正的做到 No Adapter。
它的优点如下:
在project的根build.gradle里添加maven仓储
maven { url 'https://jitpack.io' }
module下的build.gradle里添加配置和依赖
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : 'xxxx' ]
}
}
}
}
dependencies{
implementation 'com.github.chenchongyu:NoAdapter-Library:vx.x.x'
annotationProcessor 'com.github.chenchongyu:NoAdapter-Compiler:vx.x.x'
}
同时,添加如下混淆配置:
-keep public class com.runningcode.noadapter.** { *; }
-keep public class * extends com.runningcode.noadapter.adpater.BaseVH
-keepattributes *Annotation*
添加@ViewHolder
注解,继承BaseVH
类,泛型里指定当前ViewHolder需要的数据类型
@ViewHolder
public class UserHolder extends BaseVH {
private ImageView imageView;
public UserHolder(ViewGroup parent) {
super(parent, R.layout.item_image);
imageView = itemView.findViewById(R.id.image);
}
@Override
public void bindData(User data) {
imageView.setImageResource(data.image);
}
}
首先要在应用初始化的时候注册一下(建议在application里)
ViewHolderRegistry.add(new ViewHolderRegistry_xxx());
ViewHolderRegistry.add(new ViewHolderRegistry_xxxxb());
其中ViewHolderRegistry_后面的字符就是你在gradle里配置的moduleName。
当然,这一步也可以省略调,你只需要在project和module的build.gradle里添加如下配置即可:
classpath 'com.github.chenchongyu:NoAdapter-Plugin:v1.0.0.1'
apply plugin: 'no-adapter-plugin'
List allList = new ArrayList();
allList.add(new User());
NoAdapter adapter = new NoAdapter(allList);
mRecyclerView.setAdapter(adapter);
如上,一个列表页就开发完了,是不是很简单?
1.添加ItemClick事件
NoAdapter adapter = newNoAdapter(allList);
adapter.setListener(newOnItemClickListener() {
@Override
publicvoidonItemClick(Object data) {
}
});
2.如果列表的多类型卡片不是通过数据类型来区分的,而是通过同一个数据类型里的不同字段来区分的?那怎么办?
比如,一个聊天消息列表,根据类型的不同显示文字消息、语音消息、图片消息等,返回的数据结构如下:
Class ChatMsg{
publicString msg;
publicinttype; // 0:文字;1:语音;2:图片
}
在以前的开发过程中,开发者可以自己根据业务在adapter的getItemType()里返回对应卡片的类型,然后根据类型不同来创建不同的卡片。
现在我们已经没有Adapter了,那我们要怎么处理呢 ?
直接上代码:
@ViewHolder(cls = ChatMsg.class, filed = "type", type = 0)
public class TextMsgViewHolder extends BaseVH {
private TextView textView;
private ImageView imageView;
public News1ViewHolder(@NonNull ViewGroup parent) {
super(parent, R.layout.item_msg_text);
textView = itemView.findViewById(R.id.text);
imageView = itemView.findViewById(R.id.image);
}
@Override
public void bindData(ChatMsg data) {
textView.setText(data.title);
Glide.with(itemView.getContext()).load(data.url).into(imageView);
}
}
@ViewHolder(cls = ChatMsg.class, filed = "type", type = 1)
public class AudioMsgViewHolder extends BaseVH {
private TextView textView;
private ImageView imageView;
public News1ViewHolder(@NonNull ViewGroup parent) {
super(parent, R.layout.item_msg_audio);
textView = itemView.findViewById(R.id.text);
imageView = itemView.findViewById(R.id.image);
}
@Override
public void bindData(ChatMsg data) {
textView.setText(data.title);
Glide.with(itemView.getContext()).load(data.url).into(imageView);
}
}
可以看到,还是在viewholder里添加如下注解
@ViewHolder(cls = ChatMsg.class, filed = "type", type = 1)
只是在注解里标明对应的数据类型、字段名和字段值即可。
完整demo工程请移步:NoAdapter-Example
基本原理就是通过APT和反射,我们可以通过@ViewHolder注解收集到所有的ViewHolder以及其对应的泛型,把它注册到我们自己的注册中心,然后再使用NoAdapter的时候,通过注册中心类获取ItemType、ViewHolder类即可。
通过实现一个Adapter我们知道,其基本操作就是继承 RecyclerView.Adapter
,同时重写onCreateViewHolder、onBindViewHolder和getItemCount等方法,如果你的列表有多重类型的item,那么还要再重写getItemViewType方法,通过观察我们可以发现,getItemViewType返回的是一个代表item类型的int值,然后onCreateViewHolder通过这个类型来创建ViewHolder,然后在onBindViewHolder里向ViewHolder
bind数据。这里边最关键的有两点——如何确定itemType,以及如何通过itemType来确定ViewHolder?通常我们给Adapter传递的数据集合里都是List这种类型,那么其实这个集合里有几种类型(对应的class)是可以确定的,我们只需要把class转成getItemViewType需要的int类型就可以了,这点通过Object的hashCode方法就可以做到。
确定了itemType之后,如何通过ItemType来确定ViewHolder呢?其实在我们开发的时候,每个ViewHolder是知道自己要处理何种数据(Class)的,那么我们就可以通过一个注册中心,把所有的ClassType和ViewHolder一一对应起来,这样我们就可以通过ItemType来找到ViewHolder了。
@ViewHolder
public class UserHolder extends BaseVH{}
通过如上的声明,我们可以在编译期就找到所有的ViewHolder类以及其需求的Object类,这样就可以填充我们的注册中心了。
有了注册中心之后,Adapter就完全可以自动化了,关键代码如下:
// 通过注册中心查找itemType
public int getItemViewType(int position) {
return ViewHolderRegistry.getItemViewType(dataList.get(position));
}
// 通过ItemType查找ViewHolder
public BaseVH> onCreateViewHolder(ViewGroup parent, int viewType) {
Class> vhClass = ViewHolderRegistry.getVHClass(viewType);
Constructor> constructor = vhClass.getConstructor(ViewGroup.class);
BaseVH> viewHolder = (BaseVH>) constructor.newInstance(parent);
return viewHolder;
}
// 给ViewHolder 设置数据
public void onBindViewHolder(BaseVH holder, int position) {
final Object data = dataList.get(position);
holder.bindData(data);
}
更多信息请查看 NoAdapter-Library
QQ讨论群:984012228