前言

我们一般编写listView的时候顺序是这样的:
1.需要展示的数据集List
2.为这个数据集编写一个ListView
3.为这个ListView编写一个Adapter,一般继承自BaseAdapter
4.在BaseAdapter内部编写一个ViewHolder类,对应ListView里面的item控件,提高控件的查询效率
<mark>分析:</mark>
List:ListView --> Adapter extends BaseAdapter --> ViewHolder
一般情况下,一个ListView对应一个Adapter类,对应一个ViewHolder类,那如果一个app中有20个ListView,我们岂不是要写20遍?所以的做法是:

  1. 抽取ViewHolder,作为公共的类。
  2. 将Adapter封装成CommonAdapter,作为公共的类。

源代码:

(1)activity_main.xml

<?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:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

(2) item_listview.xml.xml:(ListView中单个item的布局)

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dp">

    <TextView
        android:id="@+id/titleTv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:text="Android新技能"
        android:textColor="#444"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/descTv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/titleTv"
        android:layout_marginTop="10dp"
        android:maxLines="2"
        android:minLines="1"
        android:text="Android为ListView和GridView打造万能适配器"
        android:textColor="#898989"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/timeTv"
        android:paddingTop="3dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/descTv"
        android:layout_marginTop="10dp"
        android:text="2020-03-29"
        android:textColor="#898989"
        android:textSize="12sp" />

    <TextView
        android:padding="2dp"
        android:id="@+id/phoneTv"
        android:gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/descTv"
        android:layout_marginTop="10dp"
        android:background="#2ED667"
        android:drawablePadding="5dp"
        android:text="10086"
        android:textColor="#ffffff"
        android:textSize="12sp"
        android:layout_alignParentRight="true" />


</RelativeLayout>

其对应的布局效果如下:

(3)Bean.java:数据集

public class Bean {
   
    private String title;
    private String desc;
    private String time;
    private String phone;

    public Bean() {
   
    }

    public Bean(String title, String desc, String time, String phone) {
   
        this.title = title;

        this.desc = desc;
        this.time = time;
        this.phone = phone;
    }

    public String getTitle() {
   
        return title;
    }

    public void setTitle(String title) {
   
        this.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 getPhone() {
   
        return phone;
    }

    public void setPhone(String phone) {
   
        this.phone = phone;
    }
}

(4)【可复用的代码】ViewHolder.java:

public class ViewHolder {
   

    private SparseArray<View> mViews;
    private int mPosition;
    private View mConvertView;

    public ViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
   
        this.mPosition = position;
        this.mViews = new SparseArray<View>();

        mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);

        mConvertView.setTag(this);

    }

    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; //即使ViewHolder是复用的,但是position记得更新一下
            return holder;
        }
    }

    /* 通过viewId获取控件 */
    //使用的是泛型T,返回的是View的子类
    public <T extends View> 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 getConvertView() {
   
        return mConvertView;
    }

}

(5)【可复用的代码】ListViewAdapter.java:自定义的通用适配器,继承自BaseAdapter。以后如果是自定义ListView的adapter,继承它就行了

public abstract class ListViewAdapter<T> extends BaseAdapter {
   

    //为了让子类访问,于是将属性设置为protected
    protected Context mContext;
    protected List<T> mDatas;
    protected LayoutInflater mInflater;
    private int layoutId; //不同的ListView的item布局肯能不同,所以要把布局单独提取出来

    public ListViewAdapter(Context context, List<T> datas, int layoutId) {
   
        this.mContext = context;
        mInflater = LayoutInflater.from(context);
        this.mDatas = datas;
        this.layoutId = 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 holder = ViewHolder.get(mContext, convertView, parent, layoutId, position);//layoutId就是单个item的布局

        convert(holder, getItem(position));
        return holder.getConvertView(); //这一行的代码要注意了
    }

    //将convert方法公布出去
    public abstract void convert(ViewHolder holder, T t);

}

(6) ListViewAdapterWithViewHolder.java:继承自ListViewAdapter

public class ListViewAdapterWithViewHolder extends ListViewAdapter<Bean> {
   

    //MyAdapter需要一个Context,通过Context获得Layout.inflater,然后通过inflater加载item的布局
    public ListViewAdapterWithViewHolder(Context context, List<Bean> datas) {
   
        super(context, datas, R.layout.item_listview);
    }

    @Override
    public void convert(ViewHolder holder, Bean bean) {
   

        ((TextView) holder.getView(R.id.titleTv)).setText(bean.getTitle());
        ((TextView) holder.getView(R.id.descTv)).setText(bean.getDesc());
        ((TextView) holder.getView(R.id.timeTv)).setText(bean.getTime());
        ((TextView) holder.getView(R.id.phoneTv)).setText(bean.getPhone());

/* TextView tv = holder.getView(R.id.titleTv); tv.setText(...); ImageView view = getView(viewId); Imageloader.getInstance().loadImag(view.url); */
    }
}

(7) MainActivity.java

public class MainActivity extends AppCompatActivity {
   
    private ListView listView;
    private List<Bean> mDatas;

    private ListViewAdapterWithViewHolder listViewAdapterWithViewHolder;

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

        initView();
        initData();
    }


    //方法:初始化View
    private void initView() {
   
        listView = (ListView) findViewById(R.id.listView);
    }

    //方法:初始化Data
    private void initData() {
   
        mDatas = new ArrayList<Bean>();

        //将数据装到集合中去
        Bean bean = new Bean("Android新技能1", "Android为ListView和GridView打造万能适配器", "2020-03-29", "10086");
        mDatas.add(bean);

        bean = new Bean("Android新技能2", "Android为ListView和GridView打造万能适配器", "2020-03-29", "10086");
        mDatas.add(bean);

        bean = new Bean("Android新技能3", "Android为ListView和GridView打造万能适配器", "2020-03-29", "10086");
        mDatas.add(bean);

        bean = new Bean("Android新技能4", "Android为ListView和GridView打造万能适配器", "2020-03-29", "10086");
        mDatas.add(bean);

        //为数据绑定适配器
        listViewAdapterWithViewHolder = new ListViewAdapterWithViewHolder(this, mDatas);

        listView.setAdapter(listViewAdapterWithViewHolder);

    }
}

运行结果图: