不用再写RecyclerView的Adapter了,一个Adapter搞定

来源:互联网 发布:vb数组上界什么意思 编辑:程序博客网 时间:2024/05/17 06:00

不用再写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'
  • 1
  • 1

在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;}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

为了避免数据污染,我们可以做一个Delegate类,来包裹这个User

@DelegateInfo(layoutId = R.layout.xxx, holderClass = UserHolder.class)public class UserDelegate extends AnnotationDelegate<User> {    public UserDelegate (User user) {        super(user);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

之所以要这么做,是为了保护user数据,比如说列表的选中状态,就不应该存在User中,我们可以在UserDelegate中进行记录。 
另外就是UserHolder类

public class UserHolder extends AbsViewHolder<UserDelegate> {    @Override    public void onBindView(Context context, UserDelegate userDelegate, int position, DelegateAdapter adapter) {        //进行数据绑定和事件绑定    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

DelegateAdapter应该这样使用

DelegateAdapter adapter = new DelegateAdapter (context);adapter.add (new UserDelegate(user));adapter.notifyDateSetChanged();
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

这样下来,数据就可以显示了,不需要再自定义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;  //DataSource    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);    }    /**     * make an {@link AbsViewHolder} instance for {@param viewType} with {@param itemView}     * @param viewType     * @param itemView     * @return     */    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");        }        /*if (type != 0) {            if (mTypeHolderMap.indexOfKey(type) > 0 && mTypeHolderMap.get(type) != null) {            }        }*/        Class<? extends AbsViewHolder> holderClass = impl.getHolderClass();        if (holderClass == null) {            holderClass = AnnotationDelegate.getHolderClassFromAnnotation(impl);        }        /*if (holderClass == null) {            throw new IllegalStateException("holderClass not be defined by class(" + impl.getClass().getName()                    + "), please define a holderClass by getHolderClass or HolderClass or LayoutInfo");        }*/        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了。

1
0 0