【转】Android ListView加载不同的item布局

时间:2021-07-31 13:18:03
原创教程,转载请保留出处: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的样式,得到所有的样式数量
下面直接上代码,就是上图的实现代码:
  1. package com.bestv.listViewTest;
  2. import java.util.ArrayList;
  3. import android.app.Activity;
  4. import android.content.Context;
  5. import android.os.Bundle;
  6. import android.util.Log;
  7. import android.view.LayoutInflater;
  8. import android.view.View;
  9. import android.view.ViewGroup;
  10. import android.widget.BaseAdapter;
  11. import android.widget.CheckBox;
  12. import android.widget.ImageView;
  13. import android.widget.LinearLayout;
  14. import android.widget.ListView;
  15. import android.widget.TextView;
  16. public class listViewTest extends Activity {
  17. /** Called when the activity is first created. */
  18. ListView listView;
  19. MyAdapter listAdapter;
  20. ArrayList<String> listString;
  21. @Override
  22. public void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.main);
  25. listView = (ListView)this.findViewById(R.id.listview);
  26. listString = new ArrayList<String>();
  27. for(int i = 0 ; i < 100 ; i++)
  28. {
  29. listString.add(Integer.toString(i));
  30. }
  31. listAdapter = new MyAdapter(this);
  32. listView.setAdapter(listAdapter);
  33. }
  34. class MyAdapter extends BaseAdapter{
  35. Context mContext;
  36. LinearLayout linearLayout = null;
  37. LayoutInflater inflater;
  38. TextView tex;
  39. final int VIEW_TYPE = 3;
  40. final int TYPE_1 = 0;
  41. final int TYPE_2 = 1;
  42. final int TYPE_3 = 2;
  43. public MyAdapter(Context context) {
  44. // TODO Auto-generated constructor stub
  45. mContext = context;
  46. inflater = LayoutInflater.from(mContext);
  47. }
  48. @Override
  49. public int getCount() {
  50. // TODO Auto-generated method stub
  51. return listString.size();
  52. }
  53. //每个convert view都会调用此方法,获得当前所需要的view样式
  54. @Override
  55. public int getItemViewType(int position) {
  56. // TODO Auto-generated method stub
  57. int p = position%6;
  58. if(p == 0)
  59. return TYPE_1;
  60. else if(p < 3)
  61. return TYPE_2;
  62. else if(p < 6)
  63. return TYPE_3;
  64. else
  65. return TYPE_1;
  66. }
  67. @Override
  68. public int getViewTypeCount() {
  69. // TODO Auto-generated method stub
  70. return 3;
  71. }
  72. @Override
  73. public Object getItem(int arg0) {
  74. // TODO Auto-generated method stub
  75. return listString.get(arg0);
  76. }
  77. @Override
  78. public long getItemId(int position) {
  79. // TODO Auto-generated method stub
  80. return position;
  81. }
  82. @Override
  83. public View getView(int position, View convertView, ViewGroup parent) {
  84. // TODO Auto-generated method stub
  85. viewHolder1 holder1 = null;
  86. viewHolder2 holder2 = null;
  87. viewHolder3 holder3 = null;
  88. int type = getItemViewType(position);
  89. //无convertView,需要new出各个控件
  90. if(convertView == null)
  91. {
  92. Log.e("convertView = ", " NULL");
  93. //按当前所需的样式,确定new的布局
  94. switch(type)
  95. {
  96. case TYPE_1:
  97. convertView = inflater.inflate(R.layout.listitem1, parent, false);
  98. holder1 = new viewHolder1();
  99. holder1.textView = (TextView)convertView.findViewById(R.id.textview1);
  100. holder1.checkBox = (CheckBox)convertView.findViewById(R.id.checkbox);
  101. Log.e("convertView = ", "NULL TYPE_1");
  102. convertView.setTag(holder1);
  103. break;
  104. case TYPE_2:
  105. convertView = inflater.inflate(R.layout.listitem2, parent, false);
  106. holder2 = new viewHolder2();
  107. holder2.textView = (TextView)convertView.findViewById(R.id.textview2);
  108. Log.e("convertView = ", "NULL TYPE_2");
  109. convertView.setTag(holder2);
  110. break;
  111. case TYPE_3:
  112. convertView = inflater.inflate(R.layout.listitem3, parent, false);
  113. holder3 = new viewHolder3();
  114. holder3.textView = (TextView)convertView.findViewById(R.id.textview3);
  115. holder3.imageView = (ImageView)convertView.findViewById(R.id.imageview);
  116. Log.e("convertView = ", "NULL TYPE_3");
  117. convertView.setTag(holder3);
  118. break;
  119. }
  120. }
  121. else
  122. {
  123. //有convertView,按样式,取得不用的布局
  124. switch(type)
  125. {
  126. case TYPE_1:
  127. holder1 = (viewHolder1) convertView.getTag();
  128. Log.e("convertView !!!!!!= ", "NULL TYPE_1");
  129. break;
  130. case TYPE_2:
  131. holder2 = (viewHolder2) convertView.getTag();
  132. Log.e("convertView !!!!!!= ", "NULL TYPE_2");
  133. break;
  134. case TYPE_3:
  135. holder3 = (viewHolder3) convertView.getTag();
  136. Log.e("convertView !!!!!!= ", "NULL TYPE_3");
  137. break;
  138. }
  139. }
  140. //设置资源
  141. switch(type)
  142. {
  143. case TYPE_1:
  144. holder1.textView.setText(Integer.toString(position));
  145. holder1.checkBox.setChecked(true);
  146. break;
  147. case TYPE_2:
  148. holder2.textView.setText(Integer.toString(position));
  149. break;
  150. case TYPE_3:
  151. holder3.textView.setText(Integer.toString(position));
  152. holder3.imageView.setBackgroundResource(R.drawable.icon);
  153. break;
  154. }
  155. return convertView;
  156. }
  157. }
  158. //各个布局的控件资源
  159. class viewHolder1{
  160. CheckBox checkBox;
  161. TextView textView;
  162. }
  163. class viewHolder2{
  164. TextView textView;
  165. }
  166. class viewHolder3{
  167. ImageView imageView;
  168. TextView textView;
  169. }
  170. }

复制代码

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

还有一种方法:

在主布局文件中包含一个LinearLayout. 并且设置android:orientation="vertical"
定义两种不同的布局文件,然后代码中,根据不同条件调用.addview向LinearLayout加载就可以了

在实践中遇到的问题:

发现在加载的时候,会出现classcastexception,不同的viewholder在转换的时候会出现这个问题,后来找了原因,有这样解释的:http://www.eoeandroid.com/thread-263957-1-1.html

所以就捕捉这个异常,

        if(convertView == null){//无convertView,需要new出各个控件

            //按当前所需的样式,确定new的布局
switch (type) {
case ARTICLE :
discussesInflater = LayoutInflater.from(discussescontext);
convertView = discussesInflater.inflate(R.layout.my_favourate_detail_item, null);
holder1.wvFavouriteDetails = (WebView)convertView.findViewById(R.id.wv_my_favourite_details);
holder1.tv_subject_my_favourite_details = (TextView)convertView.findViewById(R.id.tv_subject_my_favourite_details);
holder1.tv_more_my_favourite_details1= (TextView)convertView.findViewById(R.id.tv_more_my_favourite_details1);
holder1.tv_more_my_favourite_details2= (TextView)convertView.findViewById(R.id.tv_more_my_favourite_details2);
convertView.setTag(holder1);
break;
case COMMENT:
discussesInflater = LayoutInflater.from(discussescontext);
convertView = discussesInflater.inflate(R.layout.discussespic_item, null);
holder2.discussespic = (ImageView) convertView.findViewById(R.id.iv_discussespic);
holder2.discussestext = (TextView) convertView.findViewById(R.id.tv_discussestext);
holder2.nametext = (TextView) convertView.findViewById(R.id.tv_nametext);
holder2.discussespic = (com.haojiazhang.widget.CircularImage)convertView.findViewById(R.id.iv_discussespic);
convertView.setTag(holder2);
break;
default:
break;
}
}else {//有convertView,按样式,取得不用的布局
switch (type) {
case ARTICLE :
try {
holder1 = (ViewHolder1) convertView.getTag();
if (holder1 == null) {
holder1 = new ViewHolder1();
convertView = LayoutInflater.from(discussescontext).inflate(R.layout.my_favourate_detail_item, null);
holder1.wvFavouriteDetails = (WebView)convertView.findViewById(R.id.wv_my_favourite_details);
holder1.tv_subject_my_favourite_details = (TextView)convertView.findViewById(R.id.tv_subject_my_favourite_details);
holder1.tv_more_my_favourite_details1= (TextView)convertView.findViewById(R.id.tv_more_my_favourite_details1);
holder1.tv_more_my_favourite_details2= (TextView)convertView.findViewById(R.id.tv_more_my_favourite_details2);
convertView.setTag(holder1);
}
} catch (Exception e) {
holder1 = new ViewHolder1();
convertView = LayoutInflater.from(discussescontext).inflate(R.layout.my_favourate_detail_item, null);
holder1.wvFavouriteDetails = (WebView)convertView.findViewById(R.id.wv_my_favourite_details);
holder1.tv_subject_my_favourite_details = (TextView)convertView.findViewById(R.id.tv_subject_my_favourite_details);
holder1.tv_more_my_favourite_details1= (TextView)convertView.findViewById(R.id.tv_more_my_favourite_details1);
holder1.tv_more_my_favourite_details2= (TextView)convertView.findViewById(R.id.tv_more_my_favourite_details2);
convertView.setTag(holder1);
}
break;
case COMMENT:
try {
holder2 = (ViewHolder2) convertView.getTag();
if (holder2 == null) {
holder2 = new ViewHolder2();
convertView = LayoutInflater.from(discussescontext).inflate(R.layout.discussespic_item, null);
holder2.discussespic = (ImageView) convertView.findViewById(R.id.iv_discussespic);
holder2.discussestext = (TextView) convertView.findViewById(R.id.tv_discussestext);
holder2.nametext = (TextView) convertView.findViewById(R.id.tv_nametext);
holder2.discussespic = (com.haojiazhang.widget.CircularImage)convertView.findViewById(R.id.iv_discussespic);
convertView.setTag(holder2);
}
} catch (Exception e) {
holder2 = new ViewHolder2();
convertView = LayoutInflater.from(discussescontext).inflate(R.layout.discussespic_item, null);
holder2.discussespic = (ImageView) convertView.findViewById(R.id.iv_discussespic);
holder2.discussestext = (TextView) convertView.findViewById(R.id.tv_discussestext);
holder2.nametext = (TextView) convertView.findViewById(R.id.tv_nametext);
holder2.discussespic = (com.haojiazhang.widget.CircularImage)convertView.findViewById(R.id.iv_discussespic);
convertView.setTag(holder2);
}
break;
default:
break;
}
}

【转】Android ListView加载不同的item布局的更多相关文章

  1. android ListView加载不同布局

    今天来跟大家讨论下同一个ListView如何加载不同的布局. 老规矩,先来看效果图. 主要步骤如下 1.增加Type. 2.重写getViewTypeCount方法. 3.重写getItemViewT ...

  2. Android ListView加载更多

    先看效果: ListView的footer布局: <?xml version="1.0" encoding="utf-8"?> <Relati ...

  3. android listview 加载图片错乱&lpar;错位&rpar;

       写道 今天晚上一个朋友介绍我看了一篇文章,也是解决android中listview在加载图片错位的问题,看了之后,感觉写的很好,自己也遇到这个问题,但是又不知道从何下手,看到这篇文章后,我的问题 ...

  4. android listview 加载遇到的问题

    http://blog.csdn.net/l_serein/article/details/7706338 转载: 描述一下场景: 菜单栏上有若干分类,点击每一个分类,ListView下分根据分类显示 ...

  5. android之 listview加载性能优化ViewHolder

    在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以*的定义listview每一列的布局,但当listview有大量的数据需要加载的时候, ...

  6. &lbrack;Android&rsqb;异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3574131.html  这个可以实现ImageView异步加载 ...

  7. Android图片加载与缓存开源框架:Android Glide

    <Android图片加载与缓存开源框架:Android Glide> Android Glide是一个开源的图片加载和缓存处理的第三方框架.和Android的Picasso库类似,个人感觉 ...

  8. 一起写一个Android图片加载框架

    本文会从内部原理到具体实现来详细介绍如何开发一个简洁而实用的Android图片加载缓存框架,并在内存占用与加载图片所需时间这两个方面与主流图片加载框架之一Universal Image Loader做 ...

  9. listview加载性能优化ViewHolder

    在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以*的定义listview每一列的布局, 但当listview有大量的数据需要加载的时候 ...

随机推荐

  1. Atitit &&num;160&semi;如何让精灵控件运动

    Atitit  如何让精灵控件运动 ##让Sushi精灵动起来 上面的代码,我们创建了静态的sushiSprite,现在我们让它动起来.使它从屏幕顶部下落到屏幕底部.在addSushi方法中添加如下代 ...

  2. 地理围栏算法解析(Geo-fencing)

    地理围栏算法解析 http://www.cnblogs.com/LBSer/p/4471742.html 地理围栏(Geo-fencing)是LBS的一种应用,就是用一个虚拟的栅栏围出一个虚拟地理边界 ...

  3. Effective Java 11 Override clone judiciously

    Principles If you override the clone method in a nonfinal class, you should return an object obtaine ...

  4. html元素英文含义

    常用html标签的英语全称及简单功能描述 <a>:anchor 定义锚 <abbr>:abbreviation 定义缩写 <acronym>: 定义只取消首字母的缩 ...

  5. js中substring和substr的用法 &lpar;转&rpar;

    1.substring 方法 定义和用法 substring 方法用于提取字符串中介于两个指定下标之间的字符. 语法 stringObject.substring(start,stop) 参数     ...

  6. Decision Trees:机器学习根据大量数据&comma;已知年龄、收入、是否上海人、私家车价格的人,预测Ta是否有真实购买上海黄浦区楼房的能力—Jason niu

    from sklearn.feature_extraction import DictVectorizer import csv from sklearn import tree from sklea ...

  7. windows版 Java调用人脸识别离线sdk

    最近因工作需求在java-web服务中调用人脸识别离线sdk,主要通过JNA及JNI技术,但均未调试通过,JNA调用时出现以下异常,一直未解决,求大佬指点,导常信息如下: in BaiduFaceAp ...

  8. 【Oracle】浅析Oracle中的事务

    1. 什么是事务 在数据库中事务是工作的逻辑单元,一个事务是由一个或多个完成一组的相关行为的SQL语句组成,通过事务机制确保这一组SQL语句所作的操作要么都成功执行,完成整个工作单元操作,要么一个也不 ...

  9. Linux系统中切换用户身份su与sudo的用法与实例

    日常操作中为了避免一些误操作,更加安全地管理系统,通常使用的用户身份都为普通用户,而非root.当需要执行一些管理员命令操作时,再切换成root用户身份去执行. 普通用户切换到root用户的方式有:s ...

  10. cvc-complex-type&period;2&period;4&period;a&colon; Invalid content was found starting with element &OpenCurlyQuote;init-param’(转)

    在写xml的时候又一次总是报cvc-complex-type.2.4.a: Invalid content was found starting with element 错误,还出现小红叉,在网上找 ...