`
zxs19861202
  • 浏览: 914718 次
  • 性别: Icon_minigender_1
  • 来自: 湖北—》上海
社区版块
存档分类
最新评论

ListView 含有多个Item布局时,convertView的缓存与重用

阅读更多

最近有需求需要在listView中载入不同的listItem布局,若这样写

public View getView(int position, View convertView, ViewGroup parent) {
  View view = new Xxx(...);
  ... ...
  return view;
}

没有使用缓存的 convertView,会导致内存泄露


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

 

 

上述两个函数的作用这如它的名字,得到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的回收和重用就已经写好了,接下来只需要对你的不同的控件进行设置就行了。

分享到:
评论

相关推荐

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

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

    关于listview item重用导致图片重复加载 图片重新请求

    然而,在实现ListView时,由于其内部机制,可能会出现一个问题,即ListView中的item(列表项)重用导致图片重复加载,这不仅会消耗不必要的网络资源,还可能影响用户体验。本文将深入探讨这个问题,并提供解决方案。...

    安卓listview相关相关-listview获取网络图片缓存优化.zip

    ListView通过复用convertView(即ViewHolder模式)来提高性能,但这同时也意味着同一视图可能被多次重用,需要在每次显示时更新正确的内容。如果图片加载不当,就可能出现图片错位的问题。 针对这个问题,资料包中...

    ListView布局之View复用原理举例

    ListView的工作流程可以这样描述:它维护了一个叫做`convertView`的缓存,当需要绘制一个新的item时,首先检查缓存中是否有可用的View。如果有,就直接拿来使用,调整其内容以匹配新的数据;如果没有,才会创建一个...

    ListView的异步加载图片并缓存

    5. **图片重用机制**:ListView的一项优化技术是 convertView 重用,通过复用已经滑出屏幕的item视图,减少对象创建,提高性能。 6. **图片压缩**:为了节省内存和磁盘空间,加载图片前可以对其进行压缩,如调整...

    listview的上拉加载下拉刷新+本地缓存+简单的头尾布局

    6. 优化与性能:ListView的高效性在于其复用机制,通过convertView在滚动过程中重用不再显示的View。因此,需要确保每个列表项的布局尽可能简洁,避免复杂的嵌套布局和过多的计算,以降低内存消耗和提高滚动流畅度。...

    ListView获取item中EditText值

    1. **自定义ListView Item布局**:创建一个XML布局文件,包含一个EditText。例如,`list_item_edittext.xml`,其中包含一个EditText标签,设置其id以便在代码中引用。 2. **适配器类**:创建一个适配器类,继承自...

    listView快速滑动图片缓存实例

    ListView通过复用convertView(即视图池)来优化性能,当用户滚动时,不再显示的item会被回收再利用。因此,图片缓存需要确保在ListView滚动时能快速加载和显示正确图片,而不会因为频繁创建和销毁ImageView导致性能...

    ListView加载网络图片优化(加二级缓存使滑动流畅)

    1. 使用convertView复用机制:ListView会重用那些已经滑出屏幕的视图,避免了为每个可见项都创建新的视图对象,显著提升了滚动性能。 2. 异步加载:图片的加载应该在后台线程进行,防止阻塞UI线程。可以使用...

    listview获取网络图片缓存优化

    每个item的ImageView在重用时,需要清除之前绑定的图片资源,防止旧图片的残留。 6. 图片裁剪与压缩:根据ImageView的实际大小裁剪图片,避免加载过大尺寸的图片导致内存消耗过多。还可以在下载前对图片进行压缩,...

    android 嵌套的listview示例

    利用ListView的缓存机制,重用convertView,减少不必要的对象创建。 - 使用`HeaderView`和`FooterView`可以避免在Item中创建复杂的布局,提高效率。 - 对于子ListView,尽可能使用`RecycleView`代替`ListView`,...

    Android Activity在GridView和ListView之间切换使用一个布局实现的方法

    当需要在GridView和ListView之间切换以显示相同的数据集时,可以利用同一个布局和适配器(Adapter)来达到目的,从而提高代码复用和效率。下面我们将深入探讨如何实现这一功能。 1. **布局设计**: 在XML布局文件...

    Android之ListView列表视图和界面跳转实现

    接下来,我们需要创建一个自定义的Adapter,它将数据与ListView的视图绑定。在Java代码中,我们可以创建一个继承自`BaseAdapter`的类: ```java public class MyAdapter extends BaseAdapter { private Context ...

    ListVeiw适配器工具类+焦点抢占+convertView复用导致内容错乱

    1. 在适配器的getView()方法中,确保每次对convertView进行重用时,都先清空并重新设置所有视图的内容。避免简单地覆盖数据,而应确保每个视图元素都被正确地初始化。 2. 使用“if (convertView == null)”判断,只...

    ListView与CheckBox

    ListView为了提高性能,采用了视图复用(View Recycling)机制,即只有当前屏幕可见的item会被实例化,其他item则会重用已存在的视图,减少内存消耗。这就可能导致CheckBox的状态在滚动时出现问题,因为被复用的item...

    listview 适配器 优化 重用.zip

    1. **复用convertView时,确保正确地更新视图状态**:当重用一个View时,要清除上一次的数据,避免残留上一条数据的痕迹。 2. **减少视图的复杂性**:尽量避免在ListView项中使用复杂的布局,如嵌套的LinearLayouts...

    Android高级应用源码-listview 适配器 优化 重用.zip

    本资源"Android高级应用源码-listview 适配器 优化 重用.zip"聚焦于ListView的高级使用,包括适配器(Adapter)的实现、性能优化以及项(Item)的重用策略。以下是对这些关键知识点的详细解释: 1. **适配器...

    Android中ListView常用方式:使用BaseAdapter

    convertView是可重用的视图,如果为空则需要新建一个;否则,可以对其进行修改并返回。 在实际开发中,我们通常会创建一个继承自BaseAdapter的自定义Adapter,例如`ListAdapter`。在这个Adapter中,我们需要实现`...

    ListView基础

    - 使用convertView复用机制:ListView会缓存已经创建但不再显示的列表项,以便重用,减少内存分配。 - 动态加载:根据ListView的可见区域加载数据,避免一次性加载大量数据造成内存压力。 - 数据绑定:避免在...

Global site tag (gtag.js) - Google Analytics