不用再写RecyclerView的Adapter了,一个Adapter搞定
* 这可能是一个重复的轮子,仅供参考*
在以往的Android开发中,遇到列表,都要用到RecyclerView,这是谷歌所提倡的用来代替ListView的控件。相比ListView要更灵活,自由度更大,比如在做表格分割线的时候,就比Listview好用多了。
但是在使用RecyclerView的Adapter的时候,必须返回一个ViewHolder,类似于ListView的BaseAdapter中的getView方法。而且要根据不同的ViewType返回不同的ViewHolder,才能在一个RecyclerView中显示几个样式的数据。
为了解决这个问题,我采用了一种Model与ViewHolder直接绑定的方式去做,再也不用写Adapter了。
先上源码,Github地址:https://github.com/boybeak/DelegateAdapter
在gradle中使用这个库:
compile 'com.github.boybeak:adapter:1.0'
在github的readme中有使用方法,具体使用可以到github上去阅读。
核心类有哪些
几个核心的类:DelegateAdapter,AnnotationDelegate,AbsViewHolder,LayoutImpl。
DelegateAdapter 这就是那个万能的Adapter类。
AnnotationDelegate 最好使用这个类来包裹数据,再将包裹后的数据添加到DelegateAdapter中。
AbsViewHolder 所有的ViewHolder必须继承这个类,并且在这个类中进行数据事件绑定。
LayoutImpl 所有是数据必须都实现该接口,因为DelegateAdapter只接受实现了该接口的类作为数据来源。该接口有两个方法:1.getLayout,用来返回 一个Layout resourece ID。2.getHolderClass, 用来返回一个Class
基本使用方法
完整的使用方法请参考文章开头的github地址,其中有一些更加方便的做法。这里只介绍通用的便利的做法。
假如我们有这样一个model
public class User {
public long id;
public String name;
}
为了避免数据污染,我们可以做一个Delegate类,来包裹这个User
@DelegateInfo(layoutId = R.layout.xxx, holderClass = UserHolder.class)
public class UserDelegate extends AnnotationDelegate<User> {
public UserDelegate (User user) {
super(user);
}
}
之所以要这么做,是为了保护user数据,比如说列表的选中状态,就不应该存在User中,我们可以在UserDelegate中进行记录。
另外就是UserHolder类
public class UserHolder extends AbsViewHolder<UserDelegate> {
@Override
public void onBindView(Context context, UserDelegate userDelegate, int position, DelegateAdapter adapter) {
}
}
DelegateAdapter应该这样使用
DelegateAdapter adapter = new DelegateAdapter (context);
adapter.add (new UserDelegate(user));
adapter.notifyDateSetChanged();
这样下来,数据就可以显示了,不需要再自定义Adapter了。
这其中的关键就是在UserDelegate中的注解@DelegateInfo中了,在这个注解中,指定了使用的布局文件和ViewHolder。
下面就是贴出读取这其中布局的代码:
public static int getLayoutFromAnnotation (LayoutImpl impl) {
Class clz = impl.getClass();
int layoutID = 0;
Annotation anno = clz.getAnnotation(DelegateInfo.class);
if (anno != null) {
Class<? extends Annotation> annoClz = anno.annotationType();
try {
Method method = annoClz.getMethod("layoutID");
layoutID = (int)method.invoke(anno);
return layoutID;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
layoutID = getLayoutFromAnnotation(clz, impl);
return layoutID;
}
private static int getLayoutFromAnnotation (Class<? extends LayoutImpl> clz, LayoutImpl impl) {
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
LayoutID anno = field.getAnnotation(LayoutID.class);
if (anno != null) {
try {
return field.getInt(impl);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
通过反射来读取其中的布局文件id,实际中,还可以对类中的成员变量添加@LayoutID这样的注解来指定某个成员变量作为布局id。
读取ViewHolder的代码类似,如下:
public static Class<? extends AbsViewHolder> getHolderClassFromAnnotation (LayoutImpl impl) {
Class clz = impl.getClass();
Annotation anno = clz.getAnnotation(DelegateInfo.class);
Class<? extends AbsViewHolder> holderClass;
if (anno != null) {
Class<? extends Annotation> annoClz = anno.annotationType();
try {
Method method = annoClz.getMethod("holderClass");
holderClass = (Class<? extends AbsViewHolder>)method.invoke(anno);
return holderClass;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
holderClass = getHolderClassFromAnnotation(clz, impl);
return holderClass;
}
private static Class<? extends AbsViewHolder> getHolderClassFromAnnotation (Class<? extends AnnotationDelegate> clz, LayoutImpl impl) {
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
HolderClass anno = field.getAnnotation(HolderClass.class);
if (anno != null) {
try {
return (Class<? extends AbsViewHolder>)field.get(impl);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
然后在DelegateAdapter中,需要根据返回的layout来解析对应的ViewHolder
public class DelegateAdapter extends Adapter<AbsViewHolder> {
private Context mContext = null;
private List<LayoutImpl> mDelegateImplList = null;
private SparseArrayCompat<Class<? extends AbsViewHolder>> mTypeHolderMap = null; // key -- layout, value -- holderClass
public DelegateAdapter (Context context) {
mContext = context;
mDelegateImplList = new ArrayList<>();
mTypeHolderMap = new SparseArrayCompat<Class<? extends AbsViewHolder>>();
}
@Override
public final AbsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext).inflate(viewType, null);
return getHolder(viewType, itemView);
}
private AbsViewHolder getHolder (int viewType, View itemView) {
if (mTypeHolderMap.indexOfKey(viewType) >= 0) {
Class<? extends AbsViewHolder> clz = mTypeHolderMap.get(viewType);
if (clz != null) {
try {
Constructor<? extends AbsViewHolder> constructor = clz.getConstructor(View.class);
return constructor.newInstance(itemView);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
return onHolderClassNotFound(viewType, itemView);
}
public AbsViewHolder onHolderClassNotFound (int viewType, View itemView) {
throw new IllegalStateException("no holder found for viewType(0x" + Integer.toHexString(viewType) + ") at getHolder in " + this.getClass().getName());
}
@Override
public void onBindViewHolder(AbsViewHolder holder, int position) {
holder.onBindView(mContext, mDelegateImplList.get(position), position, this);
}
@Override
public int getItemViewType(int position) {
LayoutImpl impl = mDelegateImplList.get(position);
int type = impl.getLayout();
if (type == 0) {
type = AnnotationDelegate.getLayoutFromAnnotation(impl);
}
if (type <= 0) {
throw new IllegalStateException("layout not be defined by class(" + impl.getClass().getName()
+ "), please define a layout resource id by getLayout or LayoutID or LayoutInfo");
}
Class<? extends AbsViewHolder> holderClass = impl.getHolderClass();
if (holderClass == null) {
holderClass = AnnotationDelegate.getHolderClassFromAnnotation(impl);
}
if (mTypeHolderMap.indexOfKey(type) < 0 && holderClass != null) {
mTypeHolderMap.put(type, holderClass);
}
return type;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
其中最关键的地方在于getViewType, onCreateViewHolder,这里用layout id直接作为viewType来使用,所以布局不能够多个ViewHolder公用一个了,当然这种场景并不多见。我们在getViewType中,把已知的布局与ViewHolder class存入到mTypeHolderMap中,不用每次都要通过反射查询。
另外一个方法就是onCreateViewHolder了,实际上是返回的getHolder,该方法是通过反射AbsViewHolder的构造方法,去生成的ViewHolder类,这样我们就不用根据每一个ViewType来分别判断返回对应的ViewHolder了。