【Android】 ListView中getView的原理与解决多轮重复调用的方法
2012-04-01 22:49
771 查看
【0】ListView中getView的工作原理:
ListView中的每个Item是如何获取到的,先看一段国外网站截取的描述,学习自http://android.amberfog.com/?p=296:
[1]ListView asks adapter “give me a view” (getView) for each item of the list.(通过getView来获取每个item)
[2]A new View is returned and displayed(获取到后返回显示)
那么如果我们有大量的数据需要显示的时候,每个Item都去重复执行getView中的创建新的View的动作吗?这样做会耗费大量的资源去执行重复的事情,实际上Android为我们提供了一套重复利用的机制叫做“Recycler”:
原理简单描述下就是这样:在一个完整的ListView第一次出现时,每个Item都是Null的,getView的时候会跑到需要inflate一个Item的代码段,假设整个view只能最多显示10个item,那么当滑动到第11个Item的时候,第一个item会放入“recycler”,如果第11个Item和放入“Recycler”的item的view一致,那么就会使用"Recycler"里面的Item来显示,从而不用再重复[b]inflate一次,这样大大节省了创建View的工作,在需要显示大量数据时显得尤为重要。[/b]
工作原理的示意图如下:
那么我们用一个Demo来演示下具体的现象:
这是一个getView的方法,其他细节的Code就不显示了
[java]
view plaincopyprint?
/**
* The CallBack function that getView
*/
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
// TODO Auto-generated method stub
logger.e("This is position:"+position);
ImageView mlistitem04_image = null;
TextView mlistitem04_id = null;
TextView mlistitem04_name = null;
TextView mlistitem04_url = null;
UserShareSonglistEntity mUserShareSonglistEntity = (UserShareSonglistEntity) getItem(position);
if (mUserShareSonglistEntity != null)
{
if (null == convertView)
{
logger.d("convertView == null,Then inflate and findViewById");
convertView = mInflater.inflate(R.layout.listitem04, parent, false);
mlistitem04_image = (ImageView) convertView.findViewById(R.id.listitem04_image);
mlistitem04_id = (TextView) convertView.findViewById(R.id.listitem04_id);
mlistitem04_name = (TextView) convertView.findViewById(R.id.listitem04_name);
mlistitem04_url = (TextView) convertView.findViewById(R.id.listitem04_url);
}
else
{
logger.d("convertView != null,Then findViewById");
mlistitem04_image = (ImageView) convertView.findViewById(R.id.listitem04_image);
mlistitem04_id = (TextView) convertView.findViewById(R.id.listitem04_id);
mlistitem04_name = (TextView) convertView.findViewById(R.id.listitem04_name);
mlistitem04_url = (TextView) convertView.findViewById(R.id.listitem04_url);
}
if(null!=convertView)
{
logger.d("convertView != null,Then SetValue");
Util.setBitmap(mlistitem04_image, mUserShareSonglistEntity.getImage());
mlistitem04_id.setText("[Id]:"+mUserShareSonglistEntity.getId());
mlistitem04_name.setText("[Name]:"+mUserShareSonglistEntity.getName());
mlistitem04_url.setText(" }
}
return convertView;
}
当我们小心往下滑动一个位置,刚出现第6个Item,此时第1个还没有消失时,
这个时候只打印了一个获取第6个Item的Log,如下:可以看到第6个Item还是为null,需要inflate出来新的
[plain]
view plaincopyprint?
01-12 18:02:37.623: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:5
01-12 18:02:37.623: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById
01-12 18:02:37.633: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue<strong>
</strong>
网上有人解释说是因为ListView的Item的高度计算方法问题,试了下还是有效果的,希望之后有机会等了解原理后再来解释这个问题,我们可以先这也使用,设置之后会发现没滑动出现一个item才会调用一次getView,这样是合理的调用,而不会出现只有10个item却调用几十次这样比较tricky的事情,也节省了很多资源避免去重复做无用功。
最近做项目发现一个界面当有ListView是,getView和getCount中的log被疯狂调用。一个5个Item的ListView,getView竟然会被反复调用7组。尤其是当ItemView中需要加载图片时,很容易造成GC过多,很容易出现ANR。
原因就在于measure过程,ListView一般都会有好多个Item,而且也会同时显示若干组Item,这些Item的父元素都是这个ListView。
更具Google的解释,View在Draw的时候分成两个阶段:measure和layout,在measure阶段时主要就是为了计算两个参数:height和width。而且要注意的是,这是个递归的过程,从顶向下,DecorView开始依次调用自己子元素的measure。计算完成这两个参数后就开始layout,最后再是draw的调用。
对于ListView,当然每一个Item都会被调用measure方法,而在这个过程中getView和getCount会被调用,而且看用户的需求,可能会有很多次调用。
而为什么会有很多组次调用呢?
问题就在于在layout中的决定ListView或者它的父元素的height和width属性的定义了。fill_parent会好一点,计算方法会比较简单,只要跟父元素的大小相似就行,但是即使是fill_parent,也不能给View当饭吃,还是要计算出来具体的dip,所以measure还是会被调用,只是可能比wrap_content的少一点。至于自适应的它会一直考量它的宽和高,根据内容(也就是它的子Item)计算宽高。可能这个measure过程会反复执行,如果父元素也是wrap_content,这个过程会更加漫长。
所以,解决方法就是尽量避免自适应,除非是万不得已,固定大小或者填充的效果会比较好一些。
ListView中的每个Item是如何获取到的,先看一段国外网站截取的描述,学习自http://android.amberfog.com/?p=296:
[1]ListView asks adapter “give me a view” (getView) for each item of the list.(通过getView来获取每个item)
[2]A new View is returned and displayed(获取到后返回显示)
那么如果我们有大量的数据需要显示的时候,每个Item都去重复执行getView中的创建新的View的动作吗?这样做会耗费大量的资源去执行重复的事情,实际上Android为我们提供了一套重复利用的机制叫做“Recycler”:
原理简单描述下就是这样:在一个完整的ListView第一次出现时,每个Item都是Null的,getView的时候会跑到需要inflate一个Item的代码段,假设整个view只能最多显示10个item,那么当滑动到第11个Item的时候,第一个item会放入“recycler”,如果第11个Item和放入“Recycler”的item的view一致,那么就会使用"Recycler"里面的Item来显示,从而不用再重复[b]inflate一次,这样大大节省了创建View的工作,在需要显示大量数据时显得尤为重要。[/b]
工作原理的示意图如下:
那么我们用一个Demo来演示下具体的现象:
这是一个getView的方法,其他细节的Code就不显示了
[java]
view plaincopyprint?
/**
* The CallBack function that getView
*/
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
// TODO Auto-generated method stub
logger.e("This is position:"+position);
ImageView mlistitem04_image = null;
TextView mlistitem04_id = null;
TextView mlistitem04_name = null;
TextView mlistitem04_url = null;
UserShareSonglistEntity mUserShareSonglistEntity = (UserShareSonglistEntity) getItem(position);
if (mUserShareSonglistEntity != null)
{
if (null == convertView)
{
logger.d("convertView == null,Then inflate and findViewById");
convertView = mInflater.inflate(R.layout.listitem04, parent, false);
mlistitem04_image = (ImageView) convertView.findViewById(R.id.listitem04_image);
mlistitem04_id = (TextView) convertView.findViewById(R.id.listitem04_id);
mlistitem04_name = (TextView) convertView.findViewById(R.id.listitem04_name);
mlistitem04_url = (TextView) convertView.findViewById(R.id.listitem04_url);
}
else
{
logger.d("convertView != null,Then findViewById");
mlistitem04_image = (ImageView) convertView.findViewById(R.id.listitem04_image);
mlistitem04_id = (TextView) convertView.findViewById(R.id.listitem04_id);
mlistitem04_name = (TextView) convertView.findViewById(R.id.listitem04_name);
mlistitem04_url = (TextView) convertView.findViewById(R.id.listitem04_url);
}
if(null!=convertView)
{
logger.d("convertView != null,Then SetValue");
Util.setBitmap(mlistitem04_image, mUserShareSonglistEntity.getImage());
mlistitem04_id.setText("[Id]:"+mUserShareSonglistEntity.getId());
mlistitem04_name.setText("[Name]:"+mUserShareSonglistEntity.getName());
mlistitem04_url.setText(" }
}
return convertView;
}
[plain] view plaincopy" target=_blank>:"+mUserShareSonglistEntity.getUrl()); } } return convertView; } [code=dp-highlighter bg_plain] [plain] view plain[url=http://blog.csdn.net/kesenhoo/article/details/7196920#]copy[url=http://blog.csdn.net/kesenhoo/article/details/7196920#]print? 01-12 17:58:22.144: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:0 01-12 17:58:22.154: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.174: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.174: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:1 01-12 17:58:22.174: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.184: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.184: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:2 01-12 17:58:22.184: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.194: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.194: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:3 01-12 17:58:22.194: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.204: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.204: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:4 01-12 17:58:22.204: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.214: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue<strong> </strong> 01-12 17:58:22.144: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:0 01-12 17:58:22.154: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.174: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.174: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:1 01-12 17:58:22.174: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.184: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.184: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:2 01-12 17:58:22.184: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.194: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.194: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:3 01-12 17:58:22.194: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.204: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue 01-12 17:58:22.204: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:4 01-12 17:58:22.204: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById 01-12 17:58:22.214: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue<strong> </strong>
当我们小心往下滑动一个位置,刚出现第6个Item,此时第1个还没有消失时,
这个时候只打印了一个获取第6个Item的Log,如下:可以看到第6个Item还是为null,需要inflate出来新的
[plain]
view plaincopyprint?
01-12 18:02:37.623: ERROR/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:69 getView ] - This is position:5
01-12 18:02:37.623: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:80 getView ] - convertView == null,Then inflate and findViewById
01-12 18:02:37.633: DEBUG/MusicDemo(1272): @kesen@ [ main: Activity4Adapter.java:98 getView ] - convertView != null,Then SetValue<strong>
</strong>
[html] view plaincopyprint? <ListView android:id="@+id/activity04_list" android:layout_width="fill_parent" android:layout_height="fill_parent" <span style="font-size:13px; "><strong></strong></span><pre name="code" class="html" style="display: inline !important; ">/> <ListView android:id="@+id/activity04_list" android:layout_width="fill_parent" android:layout_height="fill_parent" <span style="font-size:13px; "><strong></strong></span><pre name="code" class="html" style="display: inline !important; ">/>
网上有人解释说是因为ListView的Item的高度计算方法问题,试了下还是有效果的,希望之后有机会等了解原理后再来解释这个问题,我们可以先这也使用,设置之后会发现没滑动出现一个item才会调用一次getView,这样是合理的调用,而不会出现只有10个item却调用几十次这样比较tricky的事情,也节省了很多资源避免去重复做无用功。
最近做项目发现一个界面当有ListView是,getView和getCount中的log被疯狂调用。一个5个Item的ListView,getView竟然会被反复调用7组。尤其是当ItemView中需要加载图片时,很容易造成GC过多,很容易出现ANR。
原因就在于measure过程,ListView一般都会有好多个Item,而且也会同时显示若干组Item,这些Item的父元素都是这个ListView。
更具Google的解释,View在Draw的时候分成两个阶段:measure和layout,在measure阶段时主要就是为了计算两个参数:height和width。而且要注意的是,这是个递归的过程,从顶向下,DecorView开始依次调用自己子元素的measure。计算完成这两个参数后就开始layout,最后再是draw的调用。
对于ListView,当然每一个Item都会被调用measure方法,而在这个过程中getView和getCount会被调用,而且看用户的需求,可能会有很多次调用。
而为什么会有很多组次调用呢?
问题就在于在layout中的决定ListView或者它的父元素的height和width属性的定义了。fill_parent会好一点,计算方法会比较简单,只要跟父元素的大小相似就行,但是即使是fill_parent,也不能给View当饭吃,还是要计算出来具体的dip,所以measure还是会被调用,只是可能比wrap_content的少一点。至于自适应的它会一直考量它的宽和高,根据内容(也就是它的子Item)计算宽高。可能这个measure过程会反复执行,如果父元素也是wrap_content,这个过程会更加漫长。
所以,解决方法就是尽量避免自适应,除非是万不得已,固定大小或者填充的效果会比较好一些。
相关文章推荐
- 【Android】ListView中getView的原理与解决多轮重复调用的方法
- 【Android】ListView中getView的原理与解决多轮重复调用的方法
- ListView中getView的原理与解决多轮重复调用的方法
- ListView中getView的原理与解决多轮重复调用的方法
- android中ListView多次刷新重复执行getView的解决方法
- Android ListView getView()方法重复调用导致position错位
- Android listview&gridview getview 方法多次调用问题解决方法
- android中ListView多次刷新重复执行getView的解决方法
- Android listview&gridview getview 方法多次调用问题解决方法
- Android listview&gridview getview 方法多次调用问题解决方法--->导致gridview错位
- Android listview&gridview getview 方法多次调用问题解决方法 并附上单页加载实现问卷调查适配器源代码
- Android 继承BaseAdapter适配器中的getView方法,导致的ListView条目出现重复解决办法(转载)
- listview重复调用getView()方法,解决
- android中ListView多次刷新重复执行getView的解决方法
- android:ListView滑动时图片重复加载,多次调用了GetView().
- Android ListView的getview()中position错位 重复调用(position重复调用)
- android之ListView多次调用getView解决办法
- Listview对次调用getView方法原因解析以及解决方法
- 关于重写的listview adapter中,在getView()方法中,打印语句时,相同的position打印了多次,重复调用问题
- android listview图片错位原理及解决方法