最近有需求需要在listView中载入不同的listItem布局,开始没有使用convertView,加载了多个item后导致了内存泄露,所以回来研究convertView在多个listItem布局时的缓存及应用,并且和大家分享
构造Adapter时,没有使用缓存的 convertView,导致内存泄露
示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view = new Xxx(...);
... ...
return view;
}
描述:
以构造ListView的BaseAdapter为例,在BaseAdapter中提供了方法:
public View getView(int position, View convertView, ViewGroup parent){ }
来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list
item的view对象会被回收,然后被用来构造新出现的最下面的list
item。这个构造过程就是由getView()方法完成的,getView()的第二个形参 View
convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。
由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。
修正示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
...
} else {
view = new Xxx(...);
...
}
return view;
}
上述代码很好的解决了内存泄露的问题,使用convertView回收一些布局供下面重构是使用。
但是如果出现如下图的需求,convertView就不太好用了,convertView在Item为单一的布局时,能够回收并重用,但是多个Item布局时,convertView的回收和重用会出现问题。
Listview中有3种Item布局,即使convertView缓存了一些布局,但是在重构时,根本不知道怎么样去让convertView返回你所需要的布局,这时你需要让adapter知道我当前有哪些布局,我重构Item时的布局选取规则,好让convertView能返回你需要的布局
需要重写一下两个函数
@Override
public int getItemViewType(int position) {}
官网解释如下,不解释了
Get the type of View that will be created by getView(int, android.view.View, android.view.ViewGroup)]getView(int, View, ViewGroup) for the specified item.
Parameters
position |
The position of the item within the adapter's data set whose view type we want. |
Returns
-
An integer representing the type of View. Two views should share the same type if one can be converted to the other in getView(int, android.view.View, android.view.ViewGroup)getView(int, View, ViewGroup).
Note: Integers must be in the range 0 to getViewTypeCount() -
1. IGNORE_ITEM_VIEW_TYPE can
also be returned.
@Override
public int getViewTypeCount() {}
Get the type of View that will be created by getView(int, android.view.View, android.view.ViewGroup)getView(int, View, ViewGroup) for the specified item.
Parameters
position |
The position of the item within the adapter's data set whose view type we want. |
Returns
-
An integer representing the type of View. Two views should share the same type if one can be converted to the other in getView(int, android.view.View, android.view.ViewGroup)getView(int, View, ViewGroup).
Note: Integers must be in the range 0 to getViewTypeCount() -
1. IGNORE_ITEM_VIEW_TYPE can
also be returned.
上述两个函数的作用这如它的名字,得到Item的样式,得到所有的样式数量
下面直接上代码,就是上图的实现代码:
-
package com.bestv.listViewTest;
-
-
import java.util.ArrayList;
-
-
import android.app.Activity;
-
import android.content.Context;
-
import android.os.Bundle;
-
import android.util.Log;
-
import android.view.LayoutInflater;
-
import android.view.View;
-
import android.view.ViewGroup;
-
import android.widget.BaseAdapter;
-
import android.widget.CheckBox;
-
import android.widget.ImageView;
-
import android.widget.LinearLayout;
-
import android.widget.ListView;
-
import android.widget.TextView;
-
-
public class listViewTest extends Activity {
-
/** Called when the activity is first created. */
-
ListView listView;
-
MyAdapter listAdapter;
-
ArrayList<String> listString;
-
-
@Override
-
public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main);
-
listView = (ListView)this.findViewById(R.id.listview);
-
listString = new ArrayList<String>();
-
for(int i = 0 ; i < 100 ; i++)
-
{
-
listString.add(Integer.toString(i));
-
}
-
listAdapter = new MyAdapter(this);
-
listView.setAdapter(listAdapter);
-
}
-
-
class MyAdapter extends BaseAdapter{
-
-
Context mContext;
-
LinearLayout linearLayout = null;
-
LayoutInflater inflater;
-
TextView tex;
-
final int VIEW_TYPE = 3;
-
final int TYPE_1 = 0;
-
final int TYPE_2 = 1;
-
final int TYPE_3 = 2;
-
-
public MyAdapter(Context context) {
-
// TODO Auto-generated constructor stub
-
mContext = context;
-
inflater = LayoutInflater.from(mContext);
-
}
-
-
@Override
-
public int getCount() {
-
// TODO Auto-generated method stub
-
return listString.size();
-
}
-
-
//每个convert view都会调用此方法,获得当前所需要的view样式
-
@Override
-
public int getItemViewType(int position) {
-
// TODO Auto-generated method stub
-
int p = position%6;
-
if(p == 0)
-
return TYPE_1;
-
else if(p < 3)
-
return TYPE_2;
-
else if(p < 6)
-
return TYPE_3;
-
else
-
return TYPE_1;
-
-
}
-
-
@Override
-
public int getViewTypeCount() {
-
// TODO Auto-generated method stub
-
return 3;
-
}
-
-
@Override
-
public Object getItem(int arg0) {
-
// TODO Auto-generated method stub
-
return listString.get(arg0);
-
}
-
-
@Override
-
public long getItemId(int position) {
-
// TODO Auto-generated method stub
-
return position;
-
}
-
-
@Override
-
public View getView(int position, View convertView, ViewGroup parent) {
-
// TODO Auto-generated method stub
-
viewHolder1 holder1 = null;
-
viewHolder2 holder2 = null;
-
viewHolder3 holder3 = null;
-
int type = getItemViewType(position);
-
-
-
//无convertView,需要new出各个控件
-
if(convertView == null)
-
{
-
Log.e("convertView = ", " NULL");
-
-
//按当前所需的样式,确定new的布局
-
switch(type)
-
{
-
case TYPE_1:
-
convertView = inflater.inflate(R.layout.listitem1, parent, false);
-
holder1 = new viewHolder1();
-
holder1.textView = (TextView)convertView.findViewById(R.id.textview1);
-
holder1.checkBox = (CheckBox)convertView.findViewById(R.id.checkbox);
-
Log.e("convertView = ", "NULL TYPE_1");
-
convertView.setTag(holder1);
-
break;
-
case TYPE_2:
-
convertView = inflater.inflate(R.layout.listitem2, parent, false);
-
holder2 = new viewHolder2();
-
holder2.textView = (TextView)convertView.findViewById(R.id.textview2);
-
Log.e("convertView = ", "NULL TYPE_2");
-
convertView.setTag(holder2);
-
break;
-
case TYPE_3:
-
convertView = inflater.inflate(R.layout.listitem3, parent, false);
-
holder3 = new viewHolder3();
-
holder3.textView = (TextView)convertView.findViewById(R.id.textview3);
-
holder3.imageView = (ImageView)convertView.findViewById(R.id.imageview);
-
Log.e("convertView = ", "NULL TYPE_3");
-
convertView.setTag(holder3);
-
break;
-
}
-
}
-
else
-
{
-
//有convertView,按样式,取得不用的布局
-
switch(type)
-
{
-
case TYPE_1:
-
holder1 = (viewHolder1) convertView.getTag();
-
Log.e("convertView !!!!!!= ", "NULL TYPE_1");
-
break;
-
case TYPE_2:
-
holder2 = (viewHolder2) convertView.getTag();
-
Log.e("convertView !!!!!!= ", "NULL TYPE_2");
-
break;
-
case TYPE_3:
-
holder3 = (viewHolder3) convertView.getTag();
-
Log.e("convertView !!!!!!= ", "NULL TYPE_3");
-
break;
-
}
<