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

不用再写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;
    }

通过反射来读取其中的布局文件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;
    }

然后在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;
    }

}

其中最关键的地方在于getViewType, onCreateViewHolder,这里用layout id直接作为viewType来使用,所以布局不能够多个ViewHolder公用一个了,当然这种场景并不多见。我们在getViewType中,把已知的布局与ViewHolder class存入到mTypeHolderMap中,不用每次都要通过反射查询。
另外一个方法就是onCreateViewHolder了,实际上是返回的getHolder,该方法是通过反射AbsViewHolder的构造方法,去生成的ViewHolder类,这样我们就不用根据每一个ViewType来分别判断返回对应的ViewHolder了。

猜你喜欢

转载自blog.csdn.net/boybeak/article/details/53841091