使用自定义Adapter的ListView优化方式
自定义Adapter最常用的方式是继承BaseAdapter抽象类,子类需要Override以下四个方法:
public int getCount()
public Object getItem(int position)
public long getItemId(int position)
public View getView(int position, View convertView, ViewGroup parent)
在绘制View时首先通过getCount()方法获得条目总数,然后逐一绘制,每当绘制一个条目就要调用一次getView()方法,假入通过getCount()的到的条目数为10,则getView()就被调用10次。
首先用最原始的方法实现一次:
activity_main.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="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/iv"
android:text="商品"
android:textSize="20sp" />
</RelativeLayout>
commodity.java
public class Commodity {
private int icon;
private String name;
public int getIcon() {
return icon;
}
public void setIcon(int icon) {
this.icon = icon;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ListView lv;
private List<Commodity> dataList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
//创建一个集合,用于装在数据对象
dataList = new ArrayList<>();
//初始化数据源 (将数据装入集合中)
for (int i = 1; i <= 100; i ++) {
//创建数据对象 (ListView中的每个条目)
Commodity commodity = new Commodity();
commodity.setIcon(R.mipmap.ic_launcher);
commodity.setName("商品 " + i);
//将上述数据对象添加到dataList中
dataList.add(commodity);
}
//创建适配器
MyAdapter adapter = new MyAdapter(this);
//视图加载适配器
lv.setAdapter(adapter);
}
private class MyAdapter extends BaseAdapter {
private Context mContext;
public MyAdapter(Context context) {
this.mContext = context;
}
@Override
public int getCount() {
return dataList.size();
}
@Override
public Object getItem(int position) {
return dataList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//加载布局文件
View view = View.inflate(mContext, R.layout.item, null);
//查找控件
ImageView iv = (ImageView) view.findViewById(R.id.iv);
TextView tv = (TextView) view.findViewById(R.id.tv);
iv.setImageResource(dataList.get(position).getIcon());
tv.setText(dataList.get(position).getName());
return view;
}
}
}
上述方式中假如有10000个item,就会在内存中存在10000个View对象,对内存的消耗很严重,在滑动中可能导致程序奔溃(但我目前没有遇到过),每执行getView()特别耗时,不仅需要使用inflate()加载布局文件,而且还要反复查找同一个控件(ID都是相同的)。
其实在设计android操作系统时,已经给开发者提供了优化方法,那就是对View的重用,其实是利用一个视图回收器对已经滑出屏幕的条目的View进行回收,然后就可以通过getView()中的参数convertView获取已回收的视图对象,并加以利用。
重用视图的方式实现一次:
修改MainActivity.java文件
public class MainActivity extends AppCompatActivity {
private ListView lv;
private List<Commodity> dataList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
//创建一个集合,用于装在数据对象
dataList = new ArrayList<>();
//初始化数据源 (将数据装入集合中)
for (int i = 1; i <= 100; i++) {
//创建数据对象 (ListView中的每个条目)
Commodity commodity = new Commodity();
commodity.setIcon(R.mipmap.ic_launcher);
commodity.setName("商品 " + i);
//将上述数据对象添加到dataList中
dataList.add(commodity);
}
//创建适配器
MyAdapter adapter = new MyAdapter(this);
//视图加载适配器
lv.setAdapter(adapter);
}
private class MyAdapter extends BaseAdapter {
private Context mContext;
public MyAdapter(Context context) {
this.mContext = context;
}
@Override
public int getCount() {
return dataList.size();
}
@Override
public Object getItem(int position) {
return dataList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
//加载布局文件
convertView = View.inflate(mContext, R.layout.item, null);
}
//查找控件
ImageView iv = (ImageView) convertView.findViewById(R.id.iv);
TextView tv = (TextView) convertView.findViewById(R.id.tv);
iv.setImageResource(dataList.get(position).getIcon());
tv.setText(dataList.get(position).getName());
return convertView;
}
}
}
上述方法避免了每次调用getView()方法时都要利用inflate加载布局文件的情况,但是还是会反复做一件重复的事情:findViewById()。
为了解决这个问题我们可以这样做:因为每次getView时查找的是同一个控件(只是控件的内容不同而已),所以可以把查找到的控件封装成对象,直接获取对象的内容即可。具体做法如下:
修改MainActivity.java文件
public class MainActivity extends AppCompatActivity {
private ListView lv;
private List<Commodity> dataList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
//创建一个集合,用于装在数据对象
dataList = new ArrayList<>();
//初始化数据源 (将数据装入集合中)
for (int i = 1; i <= 100; i++) {
//创建数据对象 (ListView中的每个条目)
Commodity commodity = new Commodity();
commodity.setIcon(R.mipmap.ic_launcher);
commodity.setName("商品 " + i);
//将上述数据对象添加到dataList中
dataList.add(commodity);
}
//创建适配器
MyAdapter adapter = new MyAdapter(this);
//视图加载适配器
lv.setAdapter(adapter);
}
private class MyAdapter extends BaseAdapter {
private Context mContext;
public MyAdapter(Context context) {
this.mContext = context;
}
@Override
public int getCount() {
return dataList.size();
}
@Override
public Object getItem(int position) {
return dataList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
//判断convertView是否为空
if (convertView == null) {
//加载布局文件
convertView = View.inflate(mContext, R.layout.item, null);
//创建一个ViewHolder对象来装载已查找到的控件
holder = new ViewHolder();
//将查找到的控件保存到holder中
holder.iv = (ImageView) convertView.findViewById(R.id.iv);
holder.tv = (TextView) convertView.findViewById(R.id.tv);
//将holder保存到convertView中,当convertView不为空的时候取出即可
convertView.setTag(holder);
} else {
//取出holder
holder = (ViewHolder) convertView.getTag();
}
//给封装在holder中的控件设置内容
holder.iv.setImageResource(dataList.get(position).getIcon());
holder.tv.setText(dataList.get(position).getName());
return convertView;
}
}
/**
* 装载控件对象的类
*/
class ViewHolder {
private ImageView iv;
private TextView tv;
}
}