封装一个自动绑定View和数据的Adapter

来源:互联网 发布:discuz cms 编辑:程序博客网 时间:2024/05/22 15:53

package com.scott.uidemo.adapter;import android.content.Context;import android.graphics.Bitmap;import android.media.Image;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.TextView;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import java.lang.reflect.Field;import java.util.ArrayList;import java.util.List;/** * author: scott * Created at scott on 2016/8/9. */public abstract class ListViewBaseAdapter<T> extends BaseAdapter {    protected List<T> mDatas;    protected Context mContext;    protected LayoutInflater mInflater;    private final String TAG = "ListViewBaseAdapter";    public ListViewBaseAdapter(List<T> datas, Context context) {        mDatas = datas;        mContext = context;        mInflater = LayoutInflater.from(context);    }    @Override    public int getCount() {        return mDatas.size();    }    @Override    public Object getItem(int position) {        return mDatas.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public int getViewTypeCount() {        return super.getViewTypeCount();    }    @Override    public int getItemViewType(int position) {        return super.getItemViewType(position);    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        BaseViewHolder bVh;
//由于parent为AbsListView实例,而如果我们inflate itemView,那们itemview的        //layoutparams为GroupParams,这样会丢失itemview根布局的margin属性,所以        //在外部添加一个Linearlayout相当于加载后itemview的layoutparams为marginparams        LinearLayout ll = new LinearLayout(mContext);        ll.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                    ViewGroup.LayoutParams.MATCH_PARENT));
if (convertView == null) {
            convertView = mInflater.inflate(onInflateItemView(getItemViewType(position)), ll, true);            bVh = onCreateViewHolder(convertView);            //保存当前itemview对应的type            bVh.viewType = getItemViewType(position);            convertView.setTag(bVh);        } else {            bVh = (BaseViewHolder) convertView.getTag();            //当当前可复用的convertview的viewtype类型和当前需要的的view的viewtype类型            //不相等时,那么无法复用该view,需要重新加载一个            if (bVh.viewType != getItemViewType(position)) {                convertView = mInflater.inflate(onInflateItemView(getItemViewType(position)), ll, true);                bVh = onCreateViewHolder(convertView);                bVh.viewType = getItemViewType(position);                convertView.setTag(bVh);            }        }        onBindViewHolder(bVh, position);        return convertView;    }    /***     * 当初始化ViewHolder会调用该方法,我们需要返回一个继承自BaseViewHolder     * 的类的对象,该对象应该包含了itemView中我们需要的控件的实例,并且这些     * 实例已经被初始化     *     * @param itemView     * @return     */    protected abstract BaseViewHolder onCreateViewHolder(View itemView);    /***     * 当我们需要为ViewHolder中的空件设置值的时候会调用该方法,该方法中我们需要根据position为     * ViewHolder中的控件设置值     *     * @param vh     * @param position     */    protected abstract void onBindViewHolder(BaseViewHolder vh, int position);    /**     * 当我们需要加载item的布局文件时会调用该方法,我们需要返回一个布局的资源ID     *     * @return     */    protected abstract int onInflateItemView(int viewType);    /***     * 所有的子类都该有一个内部的ViewHolder类继承自该类     * 继承自该类的viewholder只需将其中的view变量和对应的resId用     * BindView注解绑定即可     *     * @BindView TextView tv;     * 这样在onCreateViewHolder时只需new一个ViewHolder并返回即可     */    abstract class BaseViewHolder {        private int viewType;        View itemView;        protected BaseViewHolder(View v) {            itemView = v;            bindView();        }        /***         * 把viewholder中的view变量绑定到对应的view         */        private void bindView() {            //this 是runntime变量,只有在运行时才能确定            Class cls = this.getClass();            Field[] fields = cls.getDeclaredFields();            for (Field f : fields) {                BindView bv = f.getAnnotation(BindView.class);                if (bv == null) {                    continue;                }                f.setAccessible(true);                View v = itemView.findViewById(bv.value());                try {                    f.set(this, v);                } catch (IllegalAccessException e) {                    e.printStackTrace();                }            }        }        /***         * 将当前的数据和控件绑定,但是传入的数据的类必须在类上面标识         * BindData注解,元素值可以为空         * 而类中的字段必须标识@BindData(resId)注解         * 相当于将字段值和控件绑定         * @param data         */        protected void bindData(Object data) {            Class cls = data.getClass();            BindData bd = (BindData) cls.getAnnotation(BindData.class);            //当前传入的data没有标识BindData注解,则无法进行数据绑定            if (bd == null) {                return;            }            Field[] fields = cls.getFields();            for (Field f : fields) {                BindData bData = f.getAnnotation(BindData.class);                //当前字段没有标识BindData注解则无法绑定数据                if (bData == null) {                    continue;                }                Class vCls = getClass();                Field[] vFields = vCls.getDeclaredFields();                for (Field vf : vFields) {                    //如果当前ViewHolder中的控件字段没有绑定资源id,                    //相当于没有初始化则无法绑定数据                    BindView bv = vf.getAnnotation(BindView.class);                    if (bv == null) {                        continue;                    }                    f.setAccessible(true);                    //绑定数据                    setValue(vf, f, data, bv, bData);                }            }        }        /***         * 绑定当前数据         * @param vf viewholder 中的字段         * @param f data 中的字段         * @param data 数据         * @param bv viewholder 中view字段的BindView注解对象         * @param bData Data 中的 值字段的BindData 注解对象         */        private void setValue(Field vf, Field f, Object data                , BindView bv, BindData bData) {            //当字段的resId 和 控件的resId 相同则进行数据绑定            //当前只支持TextView 绑定String 数据            //ImageView 绑定 Bitmap 数据            if (bv.value() == bData.value()) {                if (vf.getType() == TextView.class) {                    try {                        Object o = vf.get(this);                        TextView tv = (TextView) o;                        tv.setText((String) f.get(data));                    } catch (IllegalAccessException e) {                        e.printStackTrace();                    }                }                if (vf.getType() == ImageView.class) {                    try {                        Object o = vf.get(this);                        ImageView iv = (ImageView) o;                        iv.setImageBitmap((Bitmap) f.get(data));                    } catch (IllegalAccessException e) {                        e.printStackTrace();                    }                }            }        }    }    /***     * 用于标识将资源id和控件进行绑定,元素不可为空     */    @Target(ElementType.FIELD)    @Retention(RetentionPolicy.RUNTIME)    protected @interface BindView {        int value();    }    /***     * 用于标识将数据Moudle 和 指定的资源id对应的控件进行绑定,     * 元素可为空,默认为 -1, 当标识moudle类时可以为空,但标识     * 字段时不能为空,否则无法绑定     */    @Target({ElementType.FIELD, ElementType.TYPE})    @Retention(RetentionPolicy.RUNTIME)    public @interface BindData {        int value() default -1;    }}

利用注解反射以及baseAdapter封装了一个自动将ViewHolder中的控件和资源Id绑定,并且自动将moudle中的数据和ViewHolder中数据绑定的Adapter.

用法:

moudle

package com.scott.uidemo.moudle;import android.graphics.Bitmap;import com.scott.uidemo.R;import com.scott.uidemo.adapter.ListViewBaseAdapter;/** * author: scott * Created at scott on 2016/8/9. */@ListViewBaseAdapter.BindDatapublic class TestMoudle {    @ListViewBaseAdapter.BindData(R.id.tv_content)    public String data;    @ListViewBaseAdapter.BindData(R.id.iv_content)    public Bitmap bmp;}

adapter:

package com.scott.uidemo.adapter;import android.content.Context;import android.view.View;import android.widget.ImageView;import android.widget.TextView;import com.scott.uidemo.R;import com.scott.uidemo.moudle.TestMoudle;import java.util.List;/** * author: scott * Created at scott on 2016/8/9. */public class MyTestAdapter extends ListViewBaseAdapter<TestMoudle>{    public MyTestAdapter(List<TestMoudle> datas, Context context) {        super(datas, context);    }    @Override    protected BaseViewHolder onCreateViewHolder(View itemView) {        MyHolder holder = new MyHolder(itemView);        return holder;    }    @Override    protected void onBindViewHolder(BaseViewHolder vh, int position) {        TestMoudle moudle = mDatas.get(position);        vh.bindData(moudle);    }    @Override    protected int onInflateItemView(int viewType) {        return R.layout.list_test_item;    }    final class MyHolder extends BaseViewHolder {        protected MyHolder(View v) {            super(v);        }        @BindView(R.id.tv_content)        TextView tvName;        @BindView(R.id.iv_content)        ImageView ivName;    }}

xml文件

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content" 这里的layout_width 和layout_height 以及其他的layout_xx可以直接设置,加载到listview中可以正常显示    android:orientation="vertical">    <TextView        android:id="@+id/tv_content"        android:text="测试文字"        android:layout_width="wrap_content"        android:layout_height="wrap_content" />    <ImageView        android:src="@mipmap/ic_launcher"        android:id="@+id/iv_content"        android:layout_width="wrap_content"        android:layout_height="wrap_content" /></LinearLayout>



1 0
原创粉丝点击