我们一向写的自定义适配器,无非就是继承ArrayAdapter,或者继承自BaseAdapter,然后重写4个方法,前三个方法基本相同,不同在于getView方法,getView里面为了减少绑定和View的重建,又会引入一个静态类ViewHolder,我相信下面这段代码你一点见过不少。
1 package com.example.nanchen.commonadapterforlistviewdemo;
2
3 import android.content.Context;
4 import android.view.LayoutInflater;
5 import android.view.View;
6 import android.view.ViewGroup;
7 import android.widget.BaseAdapter;
8 import android.widget.ImageView;
9 import android.widget.TextView;
10
11 import java.util.List;
12
13 /**
14 * 常见的ListView的Adapter适配器
15 * Created by 南尘 on 16-7-28.
16 */
17 public class MyListAdapter extends BaseAdapter {
18 private Context context;
19 private List<Data> list;
20
21 public MyListAdapter(Context context, List<Data> list) {
22 this.context = context;
23 this.list = list;
24 }
25
26 @Override
27 public int getCount() {
28 return list == null ? 0 : list.size();
29 }
30
31 @Override
32 public Object getItem(int position) {
33 return list.get(position);
34 }
35
36 @Override
37 public long getItemId(int position) {
38 return position;
39 }
40
41 @Override
42 public View getView(int position, View convertView, ViewGroup viewGroup) {
43 MyViewHolder holder = null;
44 if (convertView == null) {
45 convertView = LayoutInflater.from(context).inflate(R.layout.list_item,viewGroup,false);
46 holder = new MyViewHolder();
47 holder.iv = (ImageView) convertView.findViewById(R.id.item_image);
48 holder.tv = (TextView) convertView.findViewById(R.id.item_text);
49 convertView.setTag(holder);
50 } else {
51 holder = (MyViewHolder) convertView.getTag();
52 }
53 Data data = (Data) getItem(position);
54 holder.iv.setImageResource(data.getImageId());
55 holder.tv.setText(data.getText());
56 return convertView;
57 }
58
59 public static class MyViewHolder {
60 ImageView iv;
61 TextView tv;
62 }
63 }
上面是大家最常用,见到的适配器的写法,如果咱们项目中需要很多的适配器的话,咱们一个个写,想想就很浪费时间,因此咱们可以自己封装一个
仔细观察上面的Adapter,的确是前三个方法一样。我们要是可以全部抽出来就好了。所以可以抽出来,写一个泛型使其变成一个抽象的基类,继承自BaseAdapter.其子类只需要去关心其getView方法
1 public abstract class MyListAdapter<T> extends BaseAdapter {
2 private Context context;
3 private List<T> list;
4
5 public MyListAdapter(Context context, List<T> list) {
6 this.context = context;
7 this.list = list;
8 }
9
10 @Override
11 public int getCount() {
12 return list == null ? 0 : list.size();
13 }
14
15 @Override
16 public Object getItem(int position) {
17 return list.get(position);
18 }
19
20 @Override
21 public long getItemId(int position) {
22 return position;
23 }
24 }
好像没什么不对,但是这也没解决多少问题呀,要是我们在写大项目的时候还可以抽点时间出来打LOL拿个首胜什么的就更好了。
再来看看getView方法,基本都是先判断ViewHolder是否为空,为空则去Inflate一个xml文件进来,再绑定下视图,设置一个标记,不为空的时候直接引用标记。
或许这里我们可以试一下在ViewHolder上做点什么。
我们要是想把ViewHolder提取出来,只能把每一个Item都固定在ViewHolder里面,而Item又不是固定的,怎么办?
要是我们可以把这个Item直接作为参数传进来就好了,可是传控件好像不能区分,仔细一想,我们能看到一个控件对应着一个id,这个好像可以用HashMap的键值对处理。
而键值由于是Int型的,在新的java API中明确表示在键值为Integer的HashMap中我们要用SparseArray作代替,这样不仅简单,而且性能更优。
我们尝试着封装一下ViewHolder:
public class ViewHolder {
2 //现在对于int作为键的官方推荐用SparseArray替代HashMap
3 private final SparseArray<View> views;
4 private int position;
5 private View convertView;
6 private Context context;
7
8 private ViewHolder(Context context,ViewGroup parent, int layoutId, int position) {
9 this.context = context;
10 this.views = new SparseArray<>();
11 this.convertView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
12 convertView.setTag(this);
13 }
14
15 /**
16 * 拿到一个ViewHolder对象
17 */
18 public static ViewHolder get(View convertView, ViewGroup parent, int layoutId, int position) {
19 if (convertView == null) {
20 return new ViewHolder(parent.getContext(),parent, layoutId, position);
21 }
22 return (ViewHolder) convertView.getTag();
23 }
24
25 /**
26 * 通过控件的Id获取对于的控件,如果没有则加入views
27 */
28 public <T extends View> T getView(int viewId) {
29 View view = views.get(viewId);
30 if (view == null) {
31 view = convertView.findViewById(viewId);
32 views.put(viewId, view);
33 }
34 return (T) view;
35 }
36 }
这样的话我们的getView可能会变成这样。
1 @Override
2 public View getView(int position, View convertView, ViewGroup parent){
3 ViewHolder viewHolder = ViewHolder.get(convertView, parent,
4 R.layout.list_item, position);
5 TextView mTitle = viewHolder.getView(R.id.id_tv_title);
6 mTitle.setText(((Data) list.get(position)).getText());
7 //这里就不设置ImageView了
8 return viewHolder.getConvertView();
9 }
好吧。与其这样。我们不如直接写在Activity中。
并且如果我们想设置东西也许可以在Holder里面设置,我们可以试一试。
封装后的ViewHolder是这样。
1 package com.example.nanchen.commonadapterforlistviewdemo;
2
3 import android.content.Context;
4 import android.graphics.Bitmap;
5 import android.util.SparseArray;
6 import android.view.LayoutInflater;
7 import android.view.View;
8 import android.view.ViewGroup;
9 import android.widget.ImageView;
10 import android.widget.TextView;
11
12 import com.squareup.picasso.Picasso;
13
14 /**
15 * 万能适配器的ViewHolder
16 * Created by 南尘 on 16-7-28.
17 */
18 public class ViewHolder {
19 //现在对于int作为键的官方推荐用SparseArray替代HashMap
20 private final SparseArray<View> views;
21 private int position;
22 private View convertView;
23 private Context context;
24
25 private ViewHolder(Context context,ViewGroup parent, int layoutId, int position) {
26 this.context = context;
27 this.views = new SparseArray<>();
28 this.convertView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
29 convertView.setTag(this);
30 }
31
32 /**
33 * 拿到一个ViewHolder对象
34 */
35 public static ViewHolder get(View convertView, ViewGroup parent, int layoutId, int position) {
36 if (convertView == null) {
37 return new ViewHolder(parent.getContext(),parent, layoutId, position);
38 }
39 return (ViewHolder) convertView.getTag();
40 }
41
42 /**
43 * 通过控件的Id获取对于的控件,如果没有则加入views
44 */
45 public <T extends View> T getView(int viewId) {
46 View view = views.get(viewId);
47 if (view == null) {
48 view = convertView.findViewById(viewId);
49 views.put(viewId, view);
50 }
51 return (T) view;
52 }
53
54 public View getConvertView() {
55 return convertView;
56 }
57
58 /**
59 * 设置字符串
60 */
61 public ViewHolder setText(int viewId,String text){
62 TextView tv = getView(viewId);
63 tv.setText(text);
64 return this;
65 }
66
67 /**
68 * 设置图片
69 */
70 public ViewHolder setImageResource(int viewId,int drawableId){
71 ImageView iv = getView(viewId);
72 iv.setImageResource(drawableId);
73 return this;
74 }
75
76 /**
77 * 设置图片
78 */
79 public ViewHolder setImageBitmap(int viewId, Bitmap bitmap){
80 ImageView iv = getView(viewId);
81 iv.setImageBitmap(bitmap);
82 return this;
83 }
84
85 /**
86 * 设置图片
87 */
88 public ViewHolder setImageByUrl(int viewId,String url){
89 Picasso.with(context).load(url).into((ImageView) getView(viewId));
90 // ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(context));
91 // ImageLoader.getInstance().displayImage(url, (ImageView) getView(viewId));
92 return this;
93 }
94
95 public int getPosition(){
96 return position;
97 }
98 }
上面的图片网络加载我用Picasso加载框架,这个网上很多图片加载框架
再看看我们的万能适配器,这里我们把它写做一个抽象类,传入一个泛型作为参数。
package com.example.nanchen.commonadapterforlistviewdemo;
2
3 import android.content.Context;
4 import android.view.View;
5 import android.view.ViewGroup;
6 import android.widget.BaseAdapter;
7
8 import java.util.List;
9
10 /**
11 * 打造ListView的万能适配器
12 * Created by 南尘 on 16-7-28.
13 */
14 public abstract class CommonAdaper<T> extends BaseAdapter {
15 private Context context;
16 private List<T> list;
17
18
19 public CommonAdaper(Context context, List<T> list) {
20 this.context = context;
21 this.list = list;
22 }
23
24 @Override
25 public int getCount() {
26 return list == null ? 0 : list.size();
27 }
28
29 @Override
30 public T getItem(int position) {
31 return list.get(position);
32 }
33
34 @Override
35 public long getItemId(int position) {
36 return position;
37 }
38
39 @Override
40 public View getView(int i, View view, ViewGroup viewGroup) {
41 ViewHolder holder = ViewHolder.get(view,viewGroup,R.layout.list_item,i);
42 convert(holder,getItem(i));
43 return holder.getConvertView();
44 }
45
46 public abstract void convert(ViewHolder holder,T item);
47
48 }
用法的话就更简单了
package com.example.nanchen.commonadapterforlistviewdemo;
2
3 import android.support.v7.app.AppCompatActivity;
4 import android.os.Bundle;
5 import android.widget.ListView;
6
7 import java.util.ArrayList;
8 import java.util.List;
9
10 public class MainActivity extends AppCompatActivity {
11
12 private ListView listView;
13 private List<Data> list;
14
15 @Override
16 protected void onCreate(Bundle savedInstanceState) {
17 super.onCreate(savedInstanceState);
18 setContentView(R.layout.activity_main);
19
20 listView = (ListView) findViewById(R.id.main_lv);
21 initList();
22
23 listView.setAdapter(new CommonAdaper<Data>(this,list) {
24 @Override
25 public void convert(ViewHolder holder, Data item) {
26 holder.setText(R.id.item_text,item.getText());
27 if (item.getImageUrl() != null){
28 holder.setImageByUrl(R.id.item_image,item.getImageUrl());
29 }else {
30 holder.setImageResource(R.id.item_image,item.getImageId());
31 }
32 }
33 });
34 }
35
36 private void initList() {
37 list = new ArrayList<>();
38 for (int i = 0; i < 5; i++) {
39 list.add(new Data("本地 "+i,R.mipmap.ic_launcher));
40 }
41
42 for (int i = 0; i < 5; i++) {
43 list.add(new Data("网络 "+i,"http://pic.cnblogs.com/face/845964/20160301162812.png"));
44 }
45 }
46 }
itme的xml布局文件
一个是Activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 tools:context="com.example.nanchen.commonadapterforlistviewdemo.MainActivity">
8
9
10 <ListView
11 android:layout_width="match_parent"
12 android:layout_height="match_parent"
13 android:id="@+id/main_lv"/>
14 </RelativeLayout>
list_item.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"
android:orientation="horizontal">
<ImageView
android:id="@+id/item_image"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/item_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="内容"/>
</LinearLayout>
完事,有问题留言交流~