Android — BaseAdapter 的使用以及优化

前言

BaseAdapter数据适配器,连接数据源与视图界面的桥梁。BaseAdapter是一个抽象类,使用BaseAdapter必须写一个类继承它,并且实现它的四个方法。首先我们应该清楚流程,当系统绘制ListView的时候,首先调用getCount()方法,获取ListView的长度,然后再调用getView()方法,根据这个长度逐一绘制ListView的每一行。也就是说,如果getCount返回1,那么就只显示一行。而getItem()和getItemId()则需要处理和取得Adapter中的数据时调用。而数据量很大的时候就需要绘制很多行,极大的消耗资源,导致ListView滑动非常慢,那应该怎么优化呢?本节我们搭配ListView使用BaseAdapter,并逐渐来优化BaseAdapter。

布局:

activity_mian.xml:也就一个ListView。

item.xml:类似新闻布局。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView1"
android:src="@mipmap/ic_launcher"
android:layout_width="60dp"
android:layout_height="60dp" />
<TextView
android:id="@+id/iv_title"
android:text="Title"
android:gravity="center"
android:textSize="25sp"
android:layout_toEndOf="@+id/imageView1"
android:layout_toRightOf="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="30dp" />
<TextView
android:id="@+id/iv_content"
android:layout_below="@+id/iv_title"
android:text="Content"
android:gravity="center_vertical"
android:textSize="20sp"
android:layout_toEndOf="@+id/imageView1"
android:layout_toRightOf="@+id/imageView1"
android:layout_width="match_parent"
android:layout_height="30dp" />
</RelativeLayout>

代码:

  1. 创建ItemBean来封装数据

ItemBean.class :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package top.omooo.admin.mybaseadapter;
/**
* Created by Omooo on 2017/8/9.
*/
public class ItemBean {
public int ItemImageResid;
public String ItemContent;
public String ItemTitle;
public ItemBean(int itemImageResid, String itemContent, String itemTitle) {
ItemImageResid = itemImageResid;
ItemContent = itemContent;
ItemTitle = itemTitle;
}
}
  1. MainActivity来生成数据源

MainActivity.class :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package top.omooo.admin.mybaseadapter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
List<ItemBean> itemBeen = new ArrayList<>();
for (int i = 0; i < 20; i++) {
itemBeen.add(new ItemBean(
R.mipmap.ic_launcher,
"我是标题"+i,
"我是内容"+i
));
}
ListView listView = (ListView) findViewById(R.id.listView1);
listView.setAdapter(new MyAdapter(this,itemBeen));
}
}
  1. 创建数据适配器

MyAdapter.class :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package top.omooo.admin.mybaseadapter;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
/**
* Created by Omooo on 2017/8/9.
*/
public class MyAdapter extends BaseAdapter {
//数据源与数据适配器绑定
private List<ItemBean> mList;
//布局装载器对象
private LayoutInflater mLayoutInflater;
//记录总共消耗的时间
private long mSumTime;
/**
* 将XML转换为View
* @param context 使用当前得Adapter的界面对象
* @param list 数据源
*/
public MyAdapter(Context context,List<ItemBean> list) {
mList=list;
mLayoutInflater=LayoutInflater.from(context);
}
@Override
public int getCount() {
return mList.size();
}
@Override
public Object getItem(int i) {
return mList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
/**
* 返回每一项的数据内容
* @param i
* @param convertView
* @param viewGroup
* @return
*/
@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
//都比式
// View view = mLayoutInflater.inflate(R.layout.item, null);
// ImageView imageView = view.findViewById(R.id.imageView1);
// TextView title = view.findViewById(R.id.iv_title);
// TextView content = view.findViewById(R.id.iv_content);
// ItemBean bean = mList.get(i);
// Log.i("233", "getView: "+ mList.get(i));
// imageView.setImageResource(mList.get(i).ItemImageResid);
// title.setText(bean.ItemTitle);
// content.setText(bean.ItemContent);
// return view;
//普通式
// if (convertView == null) {
// convertView=mLayoutInflater.inflate(R.layout.item, null);
// }
// ImageView imageView = convertView.findViewById(R.id.imageView1);
// TextView title = convertView.findViewById(R.id.iv_title);
// TextView content = convertView.findViewById(R.id.iv_content);
// ItemBean bean = mList.get(i);
// imageView.setImageResource(mList.get(i).ItemImageResid);
// title.setText(bean.ItemTitle);
// content.setText(bean.ItemContent);
// return convertView;
//文艺式
/**
* 不仅利用了ListView的缓存,更通过ViewHolder类来实现显示数据的视图的缓存,避免多次findViewById寻找控件。
*/
//计时 获取系统的纳秒时间
long start = System.nanoTime();
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView=mLayoutInflater.inflate(R.layout.item, null);
viewHolder.imageView=convertView.findViewById(R.id.imageView1);
viewHolder.title=convertView.findViewById(R.id.iv_title);
viewHolder.content=convertView.findViewById(R.id.iv_content);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) convertView.getTag();
}
ItemBean bean = mList.get(i);
viewHolder.imageView.setImageResource(mList.get(i).ItemImageResid);
viewHolder.title.setText(bean.ItemTitle);
viewHolder.content.setText(bean.ItemContent);
long end = System.nanoTime();
long dValue = end - start;
mSumTime += dValue;
Log.i("233", String.valueOf(mSumTime));
return convertView;
}
//避免重复的findViewById操作
class ViewHolder {
public ImageView imageView;
public TextView title;
public TextView content;
}
}

详解:

第一次我们使用了最传统的写法,即都比式写法。我们完全可以不用所谓的convertView和ViewHolder,直接导入布局内容就可以了,但是这样也就意味着有多少行数据就要绘制多少行ListView,这显然是不可取的,然后你可以试验一下,打印log输出convertView的内容,当我们没滚动ListView的时候,convertView的值为0,然而我们向下滚动ListView时,上面的数据不可见,下面出现新的数据项,这时convertView的值不在为空了,而是创建了一系列的convertView值。也就是说,convertView相当于一个缓存,开始为0,当有数据变为不可见,它就开始缓存它的数据,后面出来的数据只需要更新就可以了,这样做也就大大的节省了系统资源的开销。当然,我们还可以继续优化,虽然重复利用了已经绘制的view,但是需要得到其中的空间,还是要通过findViewById()来获得,所以就有了第三种写法 — 文艺式。当convertView为空时,用setTag()方法为每一个View绑定一个存放控件的ViewHolder对象。当convertView不为空,重复利用已经创建的view,使用setTag()获取绑定的ViewHolder对象,这样也就避免了findViewById()对控件的层层查询,从而快速定位到控件。

预览:

参考:

  1. 慕课网
  2. BaseAdapter优化
  3. http://www.open-open.com/lib/view/open1339485728006.html
我们一直都向往,面朝大海,春暖花开。 但是几人能做到,心中有爱,四季不败?