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

转载请保留出处:http://www.eoeandroid.com/thread-72369-1-1.html

 
 
最近有需求需要在listView中载入不同的listItem布局,开始没有使用convertView,加载了多个item后导致了内存泄露,所以回来研究convertView在多个listItem布局时的缓存及应用,并且和大家分享
构造Adapter时,没有使用缓存的 convertView,导致内存泄露
示例代码:
1 public View getView(int position, View convertView, ViewGroup parent) {
2    View view = new Xxx(...);
3    ... ...
4    return view;
5  }

 描述:   以构造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对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。
修正示例代码:
 1 public View getView(int position, View convertView, ViewGroup parent) {
 2    View view = null;
 3    if (convertView != null) {
 4    view = convertView;
 5    ...
 6    } else {
 7    view = new Xxx(...);
 8    ...
 9    }
10    return view;
11  } 

上述代码很好的解决了内存泄露的问题,使用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_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 
  3 import java.util.ArrayList;
  4 
  5 import android.app.Activity;
  6 
  7 import android.content.Context;
  8 
  9 import android.os.Bundle;
 10 
 11 import android.util.Log;
 12 
 13 import android.view.LayoutInflater;
 14 
 15 import android.view.View;
 16 
 17 import android.view.ViewGroup;
 18 
 19 import android.widget.BaseAdapter;
 20 
 21 import android.widget.CheckBox;
 22 
 23 import android.widget.ImageView;
 24 
 25 import android.widget.LinearLayout;
 26 
 27 import android.widget.ListView;
 28 
 29 import android.widget.TextView;
 30 
 31 public class listViewTest extends Activity
 32 {
 33 
 34     /** Called when the activity is first created. */
 35 
 36     ListView listView;
 37 
 38     MyAdapter listAdapter;
 39 
 40     ArrayList<String> listString;
 41 
 42     @Override
 43     public void onCreate(Bundle savedInstanceState)
 44     {
 45 
 46         super.onCreate(savedInstanceState);
 47 
 48         setContentView(R.layout.main);
 49 
 50         listView = (ListView) this.findViewById(R.id.listview);
 51 
 52         listString = new ArrayList<String>();
 53 
 54         for (int i = 0; i < 100; i++)
 55 
 56         {
 57 
 58             listString.add(Integer.toString(i));
 59 
 60         }
 61 
 62         listAdapter = new MyAdapter(this);
 63 
 64         listView.setAdapter(listAdapter);
 65 
 66     }
 67 
 68     class MyAdapter extends BaseAdapter
 69     {
 70 
 71         Context mContext;
 72 
 73         LinearLayout linearLayout = null;
 74 
 75         LayoutInflater inflater;
 76 
 77         TextView tex;
 78 
 79         final int VIEW_TYPE = 3;
 80 
 81         final int TYPE_1 = 0;
 82 
 83         final int TYPE_2 = 1;
 84 
 85         final int TYPE_3 = 2;
 86 
 87         public MyAdapter(Context context)
 88         {
 89 
 90             // TODO Auto-generated constructor stub
 91 
 92             mContext = context;
 93 
 94             inflater = LayoutInflater.from(mContext);
 95 
 96         }
 97 
 98         @Override
 99         public int getCount()
100         {
101 
102             // TODO Auto-generated method stub
103 
104             return listString.size();
105 
106         }
107 
108         //每个convert view都会调用此方法,获得当前所需要的view样式
109 
110         @Override
111         public int getItemViewType(int position)
112         {
113 
114             // TODO Auto-generated method stub
115 
116             int p = position % 6;
117 
118             if (p == 0)
119 
120                 return TYPE_1;
121 
122             else if (p < 3)
123 
124                 return TYPE_2;
125 
126             else if (p < 6)
127 
128                 return TYPE_3;
129 
130             else
131 
132                 return TYPE_1;
133 
134         }
135 
136         @Override
137         public int getViewTypeCount()
138         {
139 
140             // TODO Auto-generated method stub
141 
142             return 3;
143 
144         }
145 
146         @Override
147         public Object getItem(int arg0)
148         {
149 
150             // TODO Auto-generated method stub
151 
152             return listString.get(arg0);
153 
154         }
155 
156         @Override
157         public long getItemId(int position)
158         {
159 
160             // TODO Auto-generated method stub
161 
162             return position;
163 
164         }
165 
166         @Override
167         public View getView(int position, View convertView, ViewGroup parent)
168         {
169 
170             // TODO Auto-generated method stub
171 
172             viewHolder1 holder1 = null;
173 
174             viewHolder2 holder2 = null;
175 
176             viewHolder3 holder3 = null;
177 
178             int type = getItemViewType(position);
179 
180             //无convertView,需要new出各个控件
181 
182             if (convertView == null)
183 
184             {
185                 Log.e("convertView = ", " NULL");
186 
187                 //按当前所需的样式,确定new的布局
188 
189                 switch (type)
190 
191                 {
192 
193                     case TYPE_1:
194 
195                         convertView = inflater.inflate(R.layout.listitem1, parent, false);
196 
197                         holder1 = new viewHolder1();
198 
199                         holder1.textView = (TextView) convertView.findViewById(R.id.textview1);
200 
201                         holder1.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
202 
203                         Log.e("convertView = ", "NULL TYPE_1");
204 
205                         convertView.setTag(holder1);
206 
207                         break;
208 
209                     case TYPE_2:
210 
211                         convertView = inflater.inflate(R.layout.listitem2, parent, false);
212 
213                         holder2 = new viewHolder2();
214 
215                         holder2.textView = (TextView) convertView.findViewById(R.id.textview2);
216 
217                         Log.e("convertView = ", "NULL TYPE_2");
218 
219                         convertView.setTag(holder2);
220 
221                         break;
222 
223                     case TYPE_3:
224 
225                         convertView = inflater.inflate(R.layout.listitem3, parent, false);
226 
227                         holder3 = new viewHolder3();
228 
229                         holder3.textView = (TextView) convertView.findViewById(R.id.textview3);
230 
231                         holder3.imageView = (ImageView) convertView.findViewById(R.id.imageview);
232 
233                         Log.e("convertView = ", "NULL TYPE_3");
234 
235                         convertView.setTag(holder3);
236 
237                         break;
238 
239                 }
240 
241             }
242 
243             else
244 
245             {
246 
247                 //有convertView,按样式,取得不用的布局
248 
249                 switch (type)
250 
251                 {
252 
253                     case TYPE_1:
254 
255                         holder1 = (viewHolder1) convertView.getTag();
256 
257                         Log.e("convertView !!!!!!= ", "NULL TYPE_1");
258 
259                         break;
260 
261                     case TYPE_2:
262 
263                         holder2 = (viewHolder2) convertView.getTag();
264 
265                         Log.e("convertView !!!!!!= ", "NULL TYPE_2");
266 
267                         break;
268 
269                     case TYPE_3:
270 
271                         holder3 = (viewHolder3) convertView.getTag();
272 
273                         Log.e("convertView !!!!!!= ", "NULL TYPE_3");
274 
275                         break;
276 
277                 }
278 
279             }
280 
281             //设置资源
282 
283             switch (type)
284 
285             {
286 
287                 case TYPE_1:
288 
289                     holder1.textView.setText(Integer.toString(position));
290 
291                     holder1.checkBox.setChecked(true);
292 
293                     break;
294 
295                 case TYPE_2:
296 
297                     holder2.textView.setText(Integer.toString(position));
298 
299                     break;
300 
301                 case TYPE_3:
302 
303                     holder3.textView.setText(Integer.toString(position));
304 
305                     holder3.imageView.setBackgroundResource(R.drawable.icon);
306 
307                     break;
308 
309             }
310 
311             return convertView;
312 
313         }
314 
315     }
316 
317     //各个布局的控件资源
318 
319     class viewHolder1
320     {
321 
322         CheckBox checkBox;
323 
324         TextView textView;
325 
326     }
327 
328     class viewHolder2
329     {
330 
331         TextView textView;
332 
333     }
334 
335     class viewHolder3
336     {
337 
338         ImageView imageView;
339 
340         TextView textView;
341 
342     }
343 
344 }

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

原文地址:https://www.cnblogs.com/androidxiaoyang/p/2879510.html