一、Android适配器简介

在Android中,适配器扮演者重要的角色,是UI与Data实现绑定的一个桥梁。Adapter负责创建和显示每个项目的子View和提供对下层数据的访问。支持Adapter绑定的UI控件必须扩展AdapterView抽象类。

二、传统的ListView适配器写法

我们一向写的自定义适配器,就是继承ArrayAdapter,或者继承自BaseAdapter,然后重写4个方法,前三个方法基本相同,不同在于getView方法,getView里面为了减少绑定和View的重建,又会引入一个类ViewHolder。如图:

实现以上效果,传统的做法是这样的:

带有ListView的布局文件:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.wz.adapterdemo.MainActivity">    <ListView        android:id="@+id/listView"        android:layout_width="match_parent"        android:layout_height="match_parent"/>RelativeLayout>

然后是MainActivity.java

package com.wz.adapterdemo;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.ListView;import com.wz.adapterdemo.bean.Bean;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private ListView listView;    private List datas;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        listView = (ListView) findViewById(R.id.listView);        initDatas();        listView.setAdapter(new MyAdapter(this,datas));    }    private void initDatas(){        datas = new ArrayList<>();        Bean bean1 = new Bean("移动开发1","Android手机开发1","2016-11-02","000-123456");        datas.add(bean1);        Bean bean2 = new Bean("移动开发2","Android手机开发2","2016-11-02","000-123456");        datas.add(bean2);        Bean bean3 = new Bean("移动开发3","Android手机开发3","2016-11-02","000-123456");        datas.add(bean3);        Bean bean4 = new Bean("移动开发4","Android手机开发4","2016-11-02","000-123456");        datas.add(bean4);        Bean bean5 = new Bean("移动开发5","Android手机开发5","2016-11-02","000-123456");        datas.add(bean5);    }}

写一个bean类:

package com.wz.adapterdemo.bean;/** * Created by asus on 2016/11/2. */public class Bean {    private String Title;    private String desc;    private String time;    private String  phoneNumber;    public Bean(){    }    public Bean(String title, String desc, String time, String phoneNumber) {        Title = title;        this.desc = desc;        this.time = time;        this.phoneNumber = phoneNumber;    }    public String getTitle() {        return Title;    }    public void setTitle(String title) {        Title = title;    }    public String getDesc() {        return desc;    }    public void setDesc(String desc) {        this.desc = desc;    }    public String getTime() {        return time;    }    public void setTime(String time) {        this.time = time;    }    public String getPhoneNumber() {        return phoneNumber;    }    public void setPhoneNumber(String phoneNumber) {        this.phoneNumber = phoneNumber;    }}

然后自定义一个MyAdapter:

package com.wz.adapterdemo;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;import com.wz.adapterdemo.bean.Bean;import java.util.List;/** * Created by asus on 2016/11/2. */public class MyAdapter extends BaseAdapter {    private LayoutInflater mInflater;    private List mDatas;    public MyAdapter(){    }    public MyAdapter(Context context, List datas) {        this.mInflater = LayoutInflater.from(context);        this.mDatas = datas;    }    @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 View getView(int position, View convertView, ViewGroup parent) {        ViewHolder viewHolder = null;        if(convertView == null){            convertView = mInflater.inflate(R.layout.item_listview,parent,false);            viewHolder = new ViewHolder();            viewHolder.mTitle = (TextView) convertView.findViewById(R.id.title_tv);            viewHolder.mDesc = (TextView) convertView.findViewById(R.id.desc_tv);            viewHolder.mTime = (TextView) convertView.findViewById(R.id.time_tv);            viewHolder.mPhoneNumber = (TextView) convertView.findViewById(R.id.phone_tv);            convertView.setTag(viewHolder);        }else {            viewHolder = (ViewHolder) convertView.getTag();        }        Bean bean = mDatas.get(position);        viewHolder.mTitle.setText(bean.getTitle());        viewHolder.mDesc.setText(bean.getDesc());        viewHolder.mTime.setText(bean.getTime());        viewHolder.mPhoneNumber.setText(bean.getPhoneNumber());        return convertView;    }    private class ViewHolder{        TextView mTitle;        TextView mDesc;        TextView mTime;        TextView mPhoneNumber;    }}

还需要写一个item_listview.xml:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:padding="10dp"    android:layout_width="match_parent"    android:layout_height="wrap_content">    <RelativeLayout        android:id="@+id/rl"        android:layout_width="match_parent"        android:layout_height="match_parent">        <TextView            android:id="@+id/title_tv"            android:textSize="18sp"            android:textColor="#000000"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <TextView            android:id="@+id/desc_tv"            android:textSize="15sp"            android:layout_below="@id/title_tv"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <TextView            android:id="@+id/time_tv"            android:textSize="12sp"            android:paddingTop="5dp"            android:textColor="#890909"            android:layout_below="@id/desc_tv"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />    RelativeLayout>    <RelativeLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentRight="true"        android:layout_alignParentBottom="true">        <ImageView            android:id="@+id/imageview"            android:src="@drawable/phone"            android:layout_width="20dp"            android:layout_height="30dp" />        <TextView            android:id="@+id/phone_tv"            android:textColor="#034ef1"            android:layout_toRightOf="@id/imageview"            android:layout_centerHorizontal="true"            android:layout_centerVertical="true"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />    RelativeLayout>RelativeLayout>

如果是开发一个简单点的APP用到的ListView数量不会太多,我们只要去写几个BaseAdapter实现类就可以了。但如果有有几十个ListView,此时的你该怎么办?每个ListView都去写一个适配的Adatper类吗?当然你如果不嫌累的话也不是不可以,但如果有办法可以让自己减少很多工作量,避免做重复无意义劳动,何乐而不为呢?

三、打造ListView的万能适配器

万能适配器思想:使用模板方法设计模式其核心思想很简单:抽取重复代码!

我们在继承BaseAdapter类时,都需要去实现它里面的抽象方法(getCount, getItem, getItemId, getView),其中除了getView这个方法里需要实现的代码不同,其他的都基本一样。而这个getView方法里,我们考虑到性能的问题,我们经常会引入一个ViewHolder类,尽可能的去节省资源。那么解决问题的思路就出来了,可以把这个适配器抽取成两部分:
第一部分是解决(getCount, getItem, getItemId)方法里重复代码的问题;
第二部分是分离getView方法里使用到的ViewHolder,把它单独抽取出来成一个独立的类,利用键值对Key=>Value的方法,以控件ID去寻找对应的View对象。

仔细观察上面的传统的MyAdapter写法,getCount(), getItem(), getItemId()这三个方法都是一样,我们可以全部抽取出来。于是,可以写一个泛型使其变成一个抽象的基类,继承自BaseAdapter,然后该子类只需要去关心getView方法就行了:

package com.wz.adapterdemo;import android.content.Context;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import java.util.List;public abstract class CommonAdapter extends BaseAdapter {    private Context context;    private List list;    private int layoutId;    public CommonAdapter(Context context, List list,int layoutId) {        this.context = context;        this.list = list;        this.layoutId = layoutId;    }    @Override    public int getCount() {        return list == null ? 0:list.size();    }    @Override    public T getItem(int position) {        return list.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        ......    }}

好,实现了前面三个方法,我们再来看看getView方法,在该方法里面要先判断ViewHolder是否为空,为空则去Inflate一个xml文件进来,再绑定下视图,设置一个Tag,不为空的时候直接引用Tag。

现在,我们要把ViewHolder提取出来,但每一个item都和ViewHolder相关联,而item是需要我们自己定义的,于是,我们可以把item作为参数传入,对于item里面的控件,因为每一个控件都对应这一个id,所以可以用键值对处理,我们会先想到HashMap,但是在Android中已经为我们提供了一个性能更好的数据结构来代替HashMap,那就是SparseArray。于是,ViewHolder可以这样写:

package com.wz.adapterdemo;import android.content.Context;import android.util.SparseArray;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.TextView;public class CommonViewHolder {    private SparseArray views;    private int postion;    private View contertView;    private Context context;    /**     * 构造方法,完成传统Adapter里的创建convertView对象     */    private CommonViewHolder(Context context, View contertView,ViewGroup parent,                             int lagoutId, int postion) {        this.context = context;        this.views = new SparseArray<>();        this.contertView = LayoutInflater.from(context).inflate(lagoutId,parent,false);        this.postion = postion;        this.contertView.setTag(this);    }    /**     * 入口方法,完成传统Adapter里面实例化ViewHolder对象工作     */    public static CommonViewHolder getCommonViewHolder(Context context, View convertView, int layoutId,                                                       ViewGroup parent, int position){        if(convertView == null){            return new CommonViewHolder(context,convertView,parent,layoutId,position);        }else {            CommonViewHolder commonViewHolder = (CommonViewHolder)convertView.getTag();            /*由于ListView的复用,比如屏幕只显示5个Item,那么当下拉到第6个时会复用第1个的Item             *所以这边需要更新position*/            commonViewHolder.postion = position;            return commonViewHolder;        }    }    /**     * 根据控件Id获取对应View对象     */    public   T getView(int viewId){        View view = views.get(viewId);        if(view == null){            view = contertView.findViewById(viewId);            views.put(viewId,view);        }        return (T)view;    }    /**     * 返回设置好的ConvertView对象     */    public View getContertView(){        return contertView;    }    /**     *给TextView设置字符串     */    public CommonViewHolder setText(int viewId,String text){        TextView textView = getView(viewId);        textView.setText(text);        return this;    }    /**     *给ImageView设置图片资源     */    public CommonViewHolder setImageResource(int viewId,int drawableId){        ImageView imageView = getView(viewId);        imageView.setImageResource(drawableId);        return this;    }}

这里我们提供了一个入口方法getCommonViewHolder来得到一个ViewHolder的实例对象,若实例不存在,我们去创建并设置Tag保存,这点和先前的ViewHolder所做的事情是一样的。由于所有的控件都是View的子类,这里提供了一个getView来获取各控件的对象,在我们需要使用的时候强转成我们所需要的控件类型就可以了。

于是,我们CommonAdapter中的getView方法可以这么写:

    @Override    public View getView(int position, View convertView, ViewGroup parent) {        CommonViewHolder commonViewHolder = CommonViewHolder.getCommonViewHolder(context,convertView,layoutId,parent,position);        convert(commonViewHolder,getItem(position));        return commonViewHolder.getContertView();    }    public abstract void convert(CommonViewHolder holder,T item);

这里提供一个抽象方法convert,在调用的地方用户自己实现。

最后我们来看主方法的调用(用户根据需要重载convert方法):

package com.wz.adapterdemo;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.ListView;import com.wz.adapterdemo.bean.Bean;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    private ListView listView;    private List datas;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        listView = (ListView) findViewById(R.id.listView);        initDatas();       // listView.setAdapter(new MyAdapter(this,datas));        listView.setAdapter(new CommonAdapter(this,datas,R.layout.item_listview) {            @Override            public void convert(CommonViewHolder holder, Bean item) {                  holder.setText(R.id.title_tv,item.getTitle())                        .setText(R.id.desc_tv,item.getDesc())                        .setText(R.id.time_tv,item.getTime())                        .setText(R.id.phone_tv,item.getPhoneNumber())                        .setImageResource(R.id.imageview,R.drawable.phone);            }        });    }    private void initDatas(){        datas = new ArrayList<>();        Bean bean1 = new Bean("移动开发1","Android手机开发1","2016-11-02","000-123456");        datas.add(bean1);        Bean bean2 = new Bean("移动开发2","Android手机开发2","2016-11-02","000-123456");        datas.add(bean2);        Bean bean3 = new Bean("移动开发3","Android手机开发3","2016-11-02","000-123456");        datas.add(bean3);        Bean bean4 = new Bean("移动开发4","Android手机开发4","2016-11-02","000-123456");        datas.add(bean4);        Bean bean5 = new Bean("移动开发5","Android手机开发5","2016-11-02","000-123456");        datas.add(bean5);    }}

很明显,代码量减少很多,CommonAdapter和CommonViewHolder再也不需要修改,需要什么我们往里面直接加就可以了,这样让我们可以更为专注的去实现核心代码。当然还可以更简化一点,把这些ViewHolder.getView和setText,setImage等方法再一次封装,变成只传递控件Id和对应数据就够了,这样一来我们连类都不需要写了。于是,我们可以再定义一个Adapter继承CommonAdapter,在里面实现控件内容的设定:

package com.wz.adapterdemo;import android.content.Context;import com.wz.adapterdemo.bean.Bean;import java.util.List;public class MyCommonAdapter extends CommonAdapter {    public MyCommonAdapter(Context context, List list, int layoutId) {        super(context, list, layoutId);    }    @Override    public void convert(CommonViewHolder holder, Bean item) {        holder.setText(R.id.title_tv,item.getTitle())                .setText(R.id.desc_tv,item.getDesc())                .setText(R.id.time_tv,item.getTime())                .setText(R.id.phone_tv,item.getPhoneNumber())                .setImageResource(R.id.imageview,R.drawable.phone);    }}

在MainActivity中setAdapter:

 listView.setAdapter(new MyCommonAdapter(this,datas,R.layout.item_listview));

这样,以后如果需要使用适配器Adapter就不需要再去继承BaseAdapter了,直接继承CommonAdapter配合CommonViewHolder就可以了。

项目案例源码下载地址:
http://download.csdn.net/detail/wei_zhi/9671511

更多相关文章

  1. 《Android经验分享》周刊第4期
  2. 关于android中使用new Message的内存泄露问题
  3. Android(安卓)开发 调用图库选择图片实现和参数详解
  4. android 自定义控件学习之三 控件布局常用知识总结
  5. AndFix解析——(上)
  6. 如何编程实现开启或者关闭GPS
  7. Android音乐播放器开发
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Android(安卓)Studio共用Eclipse的Androi
  2. Android面试题集2019版(包含答案整理)
  3. Android线程
  4. Android彻底组件化—UI跳转升级改造
  5. Android的八种对话框的实现
  6. Android(安卓)文件的保存与读取之SDCard(S
  7. android与javascript交互调用
  8. 解决 Android(安卓)在Eclipse 开发中 Cla
  9. android设置横屏和竖屏的方法
  10. Android(安卓)使用Vitamio打造自己的万能