listView中多个listItem布局时,convertView缓存及使用

来源:互联网 发布:怎么注册淘宝号 编辑:程序博客网 时间:2024/05/29 21:29
原创教程,转载请保留出处:http://www.eoeandroid.com/thread-72369-1-1.html
最近有需求需要在listView中载入不同的listItem布局,开始没有使用convertView,加载了多个item后导致了内存泄露,所以回来研究convertView在多个listItem布局时的缓存及应用,并且和大家分享
构造Adapter时,没有使用缓存的 convertView,导致内存泄露
示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
  View view = new Xxx(...);
  ... ...
  return view;
}

描述:
  
以构造ListViewBaseAdapter为例,在BaseAdapter中提供了方法:


public View getView(int position, View convertView, ViewGroup parent){ }

来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list itemview对象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参 View convertView就是被缓存起来的list itemview对象(初始化时缓存中没有view对象则convertViewnull)
  
由此可以看出,如果我们不去使用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就不太好用了,convertViewItem为单一的布局时,能够回收并重用,但是多个Item布局时,convertView的回收和重用会出现问题。


无标题.png 


Listview中有3Item布局,即使convertView缓存了一些布局,但是在重构时,根本不知道怎么样去让convertView返回你所需要的布局,这时你需要让adapter知道我当前有哪些布局,我重构Item时的布局选取规则,好让convertView能返回你需要的布局
需要重写一下两个函数
@Override
public int getItemViewType(int position) {}

@Override
public int getViewTypeCount() {}

上述两个函数的作用这如它的名字,得到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;@Overridepublic 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 stubmContext = context;inflater = LayoutInflater.from(mContext);}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn listString.size();}//每个convert view都会调用此方法,获得当前所需要的view样式@Overridepublic int getItemViewType(int position) {// TODO Auto-generated method stubint p = position%6;if(p == 0)return TYPE_1;else if(p < 3)return TYPE_2;else if(p < 6)return TYPE_3;elsereturn TYPE_1;}@Overridepublic int getViewTypeCount() {// TODO Auto-generated method stubreturn 3;}@Overridepublic Object getItem(int arg0) {// TODO Auto-generated method stubreturn listString.get(arg0);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubviewHolder1 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;}}//设置资源switch(type){case TYPE_1:holder1.textView.setText(Integer.toString(position));holder1.checkBox.setChecked(true);break;case TYPE_2:holder2.textView.setText(Integer.toString(position));break;case TYPE_3:holder3.textView.setText(Integer.toString(position));holder3.imageView.setBackgroundResource(R.drawable.icon);break;}return convertView;}}//各个布局的控件资源class viewHolder1{CheckBox checkBox;TextView textView;}class viewHolder2{TextView textView;}class viewHolder3{ImageView imageView;TextView textView;}}

 在getView()中需要将不同布局进行缓存和适配,系统在判断是否有convertView时,会自动去调用getItemViewType (int position) ,查看是否已经有缓存的该类型的布局,从而进入if(convertView == null)和else{}的判断。期间需要做的是convertView.setTag(holder3),以便在convertView重用时可以直接拿到该布局的控件,holder3 = (viewHolder3) convertView.getTag()。到这一步,convertView的回收和重用就已经写好了,接下来只需要对你的不同的控件进行设置就行了。
 listViewTest.rar


原创粉丝点击