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

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.BindData
public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值