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

今天我在写项目的时候遇到了一个问题,就是我需要在ListView中使用多种布局。但是如果用之前我在教程中提到的保证效率的View重用,结果每次在convertView不为空需要重新加载之后,所有的item在找寻不同布局的时候就会乱七八糟,如果牺牲效率的话,明显感觉项目在我配置不低的手机都出现了卡顿,正苦思冥想的时候,在网上发现了这样一种解决方法,真乃神来之笔,所以转载过来分享给大家。

转载地址: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;
}

描述:
  
以构造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;
}
}

//设置资源
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的回收和重用就已经写好了,接下来只需要对你的不同的控件进行设置就行了。

转载地址:http://www.eoeandroid.com/thread-72369-1-1.html

时间: 2024-08-22 14:39:45

在listView中多个listItem布局时,convertView缓存及使用的相关文章

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

原创教程,转载请保留出处:http://www.eoeandroid.com/thread-72369-1-1.html 最近有需求需要在listView中载入不同的listItem布局,开始没有使用convertView,加载了多个item后导致了内存泄露,所以回来研究convertView在多个listItem布局时的缓存及应用,并且和大家分享 构造Adapter时,没有使用缓存的 convertView,导致内存泄露 示例代码:public View getView(int positio

android代码优化----ListView中自定义adapter的封装

[正文] [引入] 我们一般编写listView的时候顺序是这样的: 需要展示的数据集List<T> 为这个数据集编写一个ListView 为这个ListView编写一个Adapter,一般继承自BaseAdapter 在BaseAdapter内部编写一个ViewHolder类,对应ListView里面的item控件,提高控件的查询效率 分析: List<T>:ListView --> Adapter extends BaseAdapter --> ViewHolder

togglebutton-Android的ListView中使用ToggleButton时状态混乱问题

问题描述 Android的ListView中使用ToggleButton时状态混乱问题 Android自定义ListView的Adapter时,每个Item布局中使用了ToggleButton,在点击ToggleButton时会影响其他item中的ToggleButton点击事件,类似于同时触发多个ToggleButton点击事件. 有谁知道什么原因? 解决方案 Androidhttp://download.csdn.net/album/detail/1121

关于listview中的listitem往别的控件拖动的问题?

问题描述 关于listview中的listitem往别的控件拖动的问题? 关于listview中的listitem往别的控件拖动的问题,怎么在拖动以后将listitem中的内容复制过去? 解决方案 参考:http://download.csdn.net/detail/szxyong/6639449 不同的控件其实思路也是一样的. 解决方案二: ListItem控件ListItem控件 解决方案三: 循环每列,将值复制到一个新的item上 解决方案四: 拖动的时候拿到listitem里面的数据,在

applytransformation-android 中 listview实现展开行项效果时出现的问题

问题描述 android 中 listview实现展开行项效果时出现的问题 如题,具体方法网上都有,就是利用动画,将行项的高度,在规定时间内叠加或递减,applyTransformation 的参数会从0到1递增,可以在这里实现自己的操作. 问题是,写完代码后,除了最后一行之外,其他行的展开和收起都没有问题 打了断点看了一下,当操作最后一行时,applyTransformation的interpolatedTime参数一直是0.0,而不是叠加的过程. 大家之前有没有遇到类似的情况,请指教一下 解

listview-当ArrayAdapter.isEnabled返回false时, ListView中的divider消失

问题描述 当ArrayAdapter.isEnabled返回false时, ListView中的divider消失 在程序的ArrayAdapter类里面我使用ListActivity.当我重写了ArrayAdapter.areAllItemsEnabled() 和 ArrayAdapter.isEnabled()方法,在 list view 中单元格之间的 divider 就会消失.我想要dividers在单元格里都能显示. 大家知道怎么避免这个问题的出现呢? 解决方案 对某些特殊的项目,在a

column-EXT中列布局时如何使用insert方法插入组件

问题描述 EXT中列布局时如何使用insert方法插入组件 比如说有3列,每列有4行,我想在第一节的第三行后面加一个combox,也就是第一列变成了5行.... 解决方案 http://blog.csdn.net/tianxiaode/article/details/46628303

布局-listview中addHeaderView 点击事件

问题描述 listview中addHeaderView 点击事件 listview中addHeaderView 中头布局的多个点击响应事件怎么设置 解决方案 headView和footerView都可以响应onItemClick方法,headView的position为0, footerView的position最大. 不过可以给headView和footerView设置OnClickListener来覆盖OnItemClick,这样,你点击headview或者footerView将触发OnCl

监听-listview item中是有EditText改变时获取Postion?

问题描述 listview item中是有EditText改变时获取Postion? listview item中是有EditText, 怎么在监听每个EditText的时候获取这个EditText的Postion? 解决方案 getview时,可以给这个edittext的tag赋值postion,然后取出tag得到postion 解决方案二: 不是本地爸爸的话大把大把差不多你都能 解决方案三: 用ListView 不要重用ConvertView