`
lansuiyun
  • 浏览: 28545 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

listView中多个listItem布局时 convertView缓存及使用(转)

 
阅读更多

最近有需求需要在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的回收和重用会出现问题。





Listview中有3Item布局,即使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_TYPEcan 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_TYPEcan 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的回收和重用就已经写好了,接下来只需要对你的不同的控件进行设置就行了。
分享到:
评论
2 楼 a379933101 2012-07-13  
哦,没看清,原来是缓冲中的呀
1 楼 a379933101 2012-07-13  
getItemViewType有必要用吗,getItemViewType不就是判断positon吗,在getView中不是有个position吗,直接使用position判断不更直接?

相关推荐

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

    在处理多个listItem布局时,为了提高性能和效率,ListView引入了convertView机制。这个机制是通过缓存已创建但不再可视的listItem视图来避免频繁地创建新视图。下面我们将详细探讨convertView的工作原理及其在实际...

    ListView动态加载listitem

    ListView是Android平台中一个常用的控件,用于展示大量的数据列表,尤其在数据量大到无法一次性加载全部内容时,动态加载listitem(也称为“懒加载”)就显得尤为重要。这种技术可以显著提高应用的性能,减少内存...

    ListView更新ListItem的Demo

    这个“ListView更新ListItem的Demo”旨在演示如何高效地更新ListView中的单个或多个列表项,提高用户体验。通过理解并实践这个Demo,开发者可以更好地掌握ListView的工作原理以及如何优化其性能。 首先,我们来看...

    读取网络图片资源的listview

    要自定义listitem样式,首先创建一个新的XML布局文件,例如`list_item.xml`,在该文件中定义图像视图(ImageView)和其他需要展示的信息(如标题、描述等)。你可以使用LinearLayout、RelativeLayout或...

    listview.zip

    在Android开发中,ListView通常与Adapter一起使用,Adapter就像一个桥梁,将数据源与ListView的视图进行绑定,使得数据能够正确地显示在ListView的各个列表项上。 在"listview.zip"这个压缩包中,可能包含了...

    Listview加载的性能优化是如何实现的

    在Android开发中,ListView是一个非常关键的组件,用于展示大量数据的列表,用户可以根据需要定制每一行的布局。然而,当ListView需要加载大量数据时,如果没有进行适当的优化,会导致内存占用过大,严重影响应用的...

    Android ListView生成表格实例

    - 列表项(ListItem)通常由XML布局文件定义,你可以创建一个包含表格样式的布局,比如使用`TableLayout`和`TableRow`。 - 使用`TableLayout`作为容器,`TableRow`表示每一行,`TextView`或其他视图组件表示单元格...

    checkbox混乱的解决

    然而,在实现ListView时,我们可能会遇到一个常见的问题,那就是“checkbox混乱”,这通常发生在用户交互时,如点击或勾选ListView中的复选框(checkbox)。这个问题的核心在于ListView的复用机制,导致了复选状态的...

    Android编程中常用适配器及自定义适配器用法实例分析

    在Android编程中,适配器(Adapter)是一个关键概念,用于连接数据源和视图组件,特别是当数据需要在ListView、GridView或Spinner等控件中显示时。适配器的作用在于将复杂的数据结构转化为可展示的视图元素,使得...

Global site tag (gtag.js) - Google Analytics