万用适配器(封装通用的基类GeneralAdapter)

一、万用适配器简介

  我们在做Android开发的时候,经常会用到ListView这个控件,我之前写过一篇文章:BaseAdapter与ListView解析。一般来说,我们都会为每一个ListView去建立一个适配器,那这样一个大型项目,不是要建立很多个适配器?!所以,为了提高代码的复用性,我们可以打造一个通用的适配器GeneralAdapter。


二、ViewHolder和GeneralAdapter的编写

  一般我们编写Adapter时都会用到ViewHolder,通过convertView.setTag与convertView进行绑定,然后当convertView已经加载过时,直接从与之对于的ViewHolder(getTag)中取得convertView布局中的控件。也就是说,实际上们每个convertView会绑定一个ViewHolder对象,这个viewHolder主要用于帮convertView存储布局中的控件。所以我们只要写出一个通用的ViewHolder,然后对于任意的convertView,提供一个对象让其setTag即可;所以我们使用容器,专门存每个Item布局中的所有控件,而且还要能够查找出来;既然需要查找,那么ListView肯定是不行了,需要一个键值对进行保存,键为控件的Id,值为控件的引用,我们使用比Map效率更高的SparseArray这个类。

以下是ViewHolder类的代码,具体的解释写在代码注释里面:

/**
 * Created by ZYQ on 2016/10/14.
 * 一个通用的ViewHolder类
 */
public class ViewHolder {

    // 使用了SparseArray用于存储与之对于的convertView的所有的控件
    private SparseArray mViews;
    private int mPosition;
    private View mConvertView;

    public ViewHolder(Context context, ViewGroup parent, int layoutID, int position) {

        this.mPosition = position;
        this.mViews = new SparseArray();
        mConvertView = LayoutInflater.from(context).inflate(layoutID, parent, false);
        mConvertView.setTag(this);

    }

    // 拿到一个ViewHolder对象
    public static ViewHolder get(Context context, View convertView,
                                 ViewGroup parent, int layoutID, int position) {

        if (convertView == null) {
            return new ViewHolder(context, parent, layoutID, position);
        } else {
            ViewHolder holder = (ViewHolder) convertView.getTag();
            holder.mPosition = position;
            return holder;
        }
    }

    /**
     * 使用泛型T,返回值是View的一个子类
     * 通过viewID来获取控件
     * 如果没有则将控件添加到SparseArray容器中
     */
    public  T getView(int viewID) {
        View view = mViews.get(viewID);
        if (view == null) {
            view = mConvertView.findViewById(viewID);
            mViews.put(viewID, view);
        }
        return (T) view;
    }

    public View getmConvertView() {
        return mConvertView;
    }

    /**
     * 封装setText方法
     * 通过getView获得并设置TextView的值
     */
    public ViewHolder setText(int viewID, String text) {
        TextView tv = getView(viewID);
        tv.setText(text);
        return this;
    }

    /**
     * 封装setImage方法
     * 通过getView获得并设置ImageView的图片
     */
    public ViewHolder setImage(int viewID, int imageResID) {
        ImageView imageView = getView(viewID);
        imageView.setImageResource(imageResID);
        return this;
    }
}

接下来我们需要的另一个重要的类就是GeneralAdapter。这个通用的Adapter一般需要保持一个List对象,存储一个Bean的集合,不同的ListView,Bean肯定是不同的,这个GeneralAdapter肯定需要支持泛型,内部维持一个List。

GeneralAdapter的代码如下,具体的解释写在代码注释里面:

/**
 * Created by ZYQ on 2016/10/15.
 * 将GeneralAdapter写成任何适配器的抽象基类
 * 只有getView()中的convert方法是需要子类去实现的
 */
public abstract class GeneralAdapter extends BaseAdapter {

    // 使用泛型可以装载多种Bean类型
    protected List mDatas;
    protected Context mContext;
    protected LayoutInflater mInflater;
    protected int mLayoutID;

    public GeneralAdapter(Context context, List datas, int layoutID) {
        this.mContext = context;
        this.mDatas = datas;
        mInflater = LayoutInflater.from(context);
        this.mLayoutID = layoutID;
    }

    @Override

    public int getCount() {
        return mDatas.size();
    }

    @Override
    public T getItem(int position) {
        return mDatas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 实例化一个viewHolder
        ViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent, mLayoutID, position);
        convert(viewHolder, getItem(position));
        return viewHolder.getmConvertView();
    }

    // 对外公布的convert方法,并将holder和Bean对象传出去
    public abstract void convert(ViewHolder holder, T t);
}

完成了GeneralAdapter的编写之后,我们再也不需要为每一个ListView编写一个Adapter了,我们只需要在Activity里面使用匿名内部类就行了。


三、万用适配器实例测试

在activity_main里面添加listView控件,然后编写item布局:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">


    <ImageView
        android:id="@+id/imageView"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@mipmap/ic_launcher"/>

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="Title"
        android:layout_toEndOf="@+id/imageView"
        android:gravity="center"
        android:textSize="25sp"/>

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="Content what what what what"
        android:layout_toEndOf="@+id/imageView"
        android:layout_below="@id/tv_title"
        android:gravity="center_vertical"
        android:textSize="20sp"/>


RelativeLayout>

接下来,我们只需要在MainActivity中实现ListView和初始化数据就好了(真正做项目开发的时候,为了降低代码的耦合度,还是建议遵循一定的设计模式,不要将数据的处理写在Activity里面,这里只是做个测试)

public class MainActivity extends AppCompatActivity {

    private  List mDatas;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initDatas();
        initView();

    }

    private void initDatas() {

        mDatas = new ArrayList();
        for (int i = 0; i < 20; i++) {
            mDatas.add(new ItemBean(
                    R.mipmap.ic_launcher, "标题", "我是正文 我是正文 我是正文"));
        }

    }

    private void initView() {

        ListView mListView = (ListView) findViewById(R.id.listview_main);
        if (mListView != null) {
            // 使用匿名内部类简化代码
            mListView.setAdapter(new GeneralAdapter(MainActivity.this, mDatas, R.layout.item) {
                @Override
                public void convert(ViewHolder holder, ItemBean itemBean) {
                    // 使用链式编程
                    holder.setImage(R.id.imageView, R.mipmap.ic_launcher)
                            .setText(R.id.tv_title, itemBean.getItemTitle())
                            .setText(R.id.tv_content, itemBean.getItemContent());
                }
            });
        }
    }

}

最终实现的效果图还是和上一篇文章一样:

万用适配器(封装通用的基类GeneralAdapter)_第1张图片

这样一来,代码量是不是简化了不少呢?


The end! 。◕‿◕。

你可能感兴趣的:(Android)