优化listview滑动卡顿优化 有哪些方法

为了避免大家误会这个回答只是老生常谈 ListView 的重用机制,编辑一下。我这里说一下我用 ListView 的一些经验,为了尽量说的全面一些,这里列一些 Tips,具体的代码可以找相关的文章,或者一起交流: 首先,虽然大家都知道,还是提一下,利用好 convertView 来重用 View,切忌每次 getView() 都新建。ListView 的核心原理就是重用 View。ListView 中有一个回收器,Item 滑出界面的时候 View 会回收到这里,需要显示新的 Item 的时候,就尽量重用回收器里面的 View。利用好 View Type,例如你的 ListView 中有几个类型的 Item,需要给每个类型创建不同的 View,这样有利于 ListView 的回收,当然类型不能太多;尽量让 ItemView 的 Layout 层次结构简单,这是所有 Layout 都必须遵循的; 善用自定义 View,自定义 View 可以有效的减小 Layout 的层级,而且对绘制过程可以很好的控制;尽量能保证 Adapter 的 hasStableIds() 返回 true,这样在 notifyDataSetChanged() 的时候,如果 id 不变,ListView 将不会重新绘制这个 View,达到优化的目的;每个 Item 不能太高,特别是不要超过屏幕的高度,可以参考 Facebook 的优化方法,把特别复杂的 Item 分解成若干小的 Item,特别推荐看一下这个文章:为了保证 ListView 滑动的流畅性,getView() 中要做尽量少的事情,不要有耗时的操作。特别是滑动的时候不要加载图片,停下来再加载,这个库可以帮助你 Glide:使用 RecycleView 代替。 ListView 每次更新数据都要 notifyDataSetChanged(),有些太暴力了。RecycleView 在性能和可定制性上都有很大的改善,推荐使用。有时候,需要从根本上考虑,是否真的要使用 ListView 来实现你的需求,或者是否有其他选择?写的有点散,有些也是相互穿插的。这里面提到的都是一些原则,详细的解决方案,每个都能在网上找到相关的文章。
有一个小细节,很多开发人员都没有注意过&br&&br&比如你的Item中有三个按钮,你要为三个按钮分别定义点击事件,如何定义?&br&&br&也许你会在getView中这样做&br&&br&&br&&div class=&highlight&&&pre&&code class=&language-java&&&span class=&n&&button1&/span&&span class=&o&&.&/span&&span class=&na&&setOnclickListener&/span&&span class=&o&&(&/span&&span class=&k&&new&/span& &span class=&n&&View&/span&&span class=&o&&.&/span&&span class=&na&&OnClickListener&/span&&span class=&o&&()&/span& &span class=&o&&{&/span&
&span class=&nd&&@override&/span&
&span class=&kd&&public&/span& &span class=&kt&&void&/span& &span class=&nf&&onClick&/span&&span class=&o&&(&/span&&span class=&n&&View&/span& &span class=&n&&v&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&c1&&//balabalabala...&/span&
&span class=&o&&}&/span&
&span class=&o&&});&/span&
&span class=&n&&button2&/span&&span class=&o&&.&/span&&span class=&na&&setOnclickListener&/span&&span class=&o&&(&/span&&span class=&k&&new&/span& &span class=&n&&View&/span&&span class=&o&&.&/span&&span class=&na&&OnClickListener&/span&&span class=&o&&()&/span& &span class=&o&&{&/span&
&span class=&nd&&@override&/span&
&span class=&kd&&public&/span& &span class=&kt&&void&/span& &span class=&nf&&onClick&/span&&span class=&o&&(&/span&&span class=&n&&View&/span& &span class=&n&&v&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&c1&&//balabalabala...&/span&
&span class=&o&&}&/span&
&span class=&o&&});&/span&
&span class=&n&&button3&/span&&span class=&o&&.&/span&&span class=&na&&setOnclickListener&/span&&span class=&o&&(&/span&&span class=&k&&new&/span& &span class=&n&&View&/span&&span class=&o&&.&/span&&span class=&na&&OnClickListener&/span&&span class=&o&&()&/span& &span class=&o&&{&/span&
&span class=&nd&&@override&/span&
&span class=&kd&&public&/span& &span class=&kt&&void&/span& &span class=&nf&&onClick&/span&&span class=&o&&(&/span&&span class=&n&&View&/span& &span class=&n&&v&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&c1&&//balabalabala...&/span&
&span class=&o&&}&/span&
&span class=&o&&});&/span&
&/code&&/pre&&/div&&br&如果你每屏显示7个Item,你一共创建了21个listener对象在内存中,如果View回收不畅,会更多,这样,在滚动的时候频繁GC 就会导致卡顿(这里描述有误,请看7月25日更新)&br&&br&如果你在Adapter初始化的时候创建一个Listener&br&&br&&div class=&highlight&&&pre&&code class=&language-text&&public MyAdapter () {
myListener = new View.OnClickListener() {
public void onClick(View v) {
v.getTag()
//balabalabala...
&/code&&/pre&&/div&&br&通过传入的View v这个参数判断是哪一个button被点击,这样,无论View如何创建,你只创建了1个Listener对象&br&&br&这只是一个小细节,优化的方式要综合使用,才会事半功倍&br&&br&&br&------------------7月24日更-----------------&br&&br&v.getTag() 这个tag本不是用来存数据的,通俗点讲它和view 的Id是同一个东西,只不过tag的类型是Object。实际上在tag中存储数据是不符合规范的方式&br&&br&但其实View类有两种tag,&br&&br&&br&&br&&a href=&file:///Users/SamuraiSong/Library/Application%20Support/Dash/DocSets/Android/Android.docset/Contents/Resources/Documents/docs/reference/android/view/View.html#setTag(java.lang.Object)&&setTag&/a&(&a href=&file:///Users/SamuraiSong/Library/Application%20Support/Dash/DocSets/Android/Android.docset/Contents/Resources/Documents/docs/reference/java/lang/Object.html&&Object&/a& tag) 方法将tag保存在一个成员变量中,findViewWithTag正是遍历此tag&br&&br&&a href=&file:///Users/SamuraiSong/Library/Application%20Support/Dash/DocSets/Android/Android.docset/Contents/Resources/Documents/docs/reference/android/view/View.html#setTag(int,%20java.lang.Object)&&setTag&/a&(int key, &a href=&file:///Users/SamuraiSong/Library/Application%20Support/Dash/DocSets/Android/Android.docset/Contents/Resources/Documents/docs/reference/java/lang/Object.html&&Object&/a& tag) 方法是最终是调用View类中的setKeyedTag(int key, Object tag)&br&这是一个私有方法&br&&br&&br&&br&他是用 SparseArray 实现的,我们可以把需要的东西存到这里面(其实观察源码可以发现,系统很多时候都是这样做的)&br&&br&这里要注意一点,参数key必须是唯一的,那么我们可以这样做&br&那么需要先在res/values/strings.xml中添加&br&&div class=&highlight&&&pre&&code class=&language-xml&&&span class=&nt&&&resources&&/span&
&span class=&nt&&&item&/span& &span class=&na&&type=&/span&&span class=&s&&&id&&/span& &span class=&na&&name=&/span&&span class=&s&&&tag_first&&/span&&span class=&nt&&&&/item&&/span&
&span class=&nt&&&item&/span& &span class=&na&&type=&/span&&span class=&s&&&id&&/span& &span class=&na&&name=&/span&&span class=&s&&&tag_second&&/span&&span class=&nt&&&&/item&&/span&
&span class=&nt&&&/resources&&/span&
&/code&&/pre&&/div&使用的时候写成&br&&br&&div class=&highlight&&&pre&&code class=&language-java&&&span class=&n&&view&/span&&span class=&o&&.&/span&&span class=&na&&setTag&/span&&span class=&o&&(&/span&&span class=&n&&R&/span&&span class=&o&&.&/span&&span class=&na&&id&/span&&span class=&o&&.&/span&&span class=&na&&tag_first&/span&&span class=&o&&,&/span& &span class=&n&&obj1&/span&&span class=&o&&);&/span&
&span class=&n&&view&/span&&span class=&o&&.&/span&&span class=&na&&setTag&/span&&span class=&o&&(&/span&&span class=&n&&R&/span&&span class=&o&&.&/span&&span class=&na&&id&/span&&span class=&o&&.&/span&&span class=&na&&tag_second&/span&&span class=&o&&,&/span& &span class=&n&&obj2&/span&&span class=&o&&);&/span&
&/code&&/pre&&/div&&br&-------------------------关于如何绑定数据的问题-----------------&br&&br&有人问,如果这样写,所有button只能通过id区分逻辑,无法传入每个item的数据&br&&br&我们可以将数据通过view 的tag带进来&br&&br&public View getView(.....) {&br&
v.setTag(key, getItem(position));&br&
....&br&}&br&&br&然后在listener中通过v.getTag()将数据取出&br&&br&&br&-----------------------7月25日update-------------------&br&&br&这里我有一个失误&br&&br&如果listener里面的逻辑与当前的item有关,那么其实并不只是创建了21个listener对象&br&&br&&div class=&highlight&&&pre&&code class=&language-java&&&span class=&kd&&public&/span& &span class=&kt&&void&/span& &span class=&nf&&getView&/span& &span class=&o&&(&/span&&span class=&n&&View&/span& &span class=&n&&convertView&/span& &span class=&o&&,&/span&&span class=&kd&&final&/span& &span class=&kt&&int&/span& &span class=&n&&position&/span& &span class=&o&&....)&/span& &span class=&o&&{&/span&
&span class=&k&&if&/span& &span class=&o&&(&/span&&span class=&n&&convertView&/span& &span class=&o&&==&/span& &span class=&kc&&null&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&n&&View&/span& &span class=&n&&v&/span& &span class=&o&&=&/span& &span class=&n&&LayoutInflater&/span&&span class=&o&&.&/span&&span class=&na&&from&/span&&span class=&o&&(&/span&&span class=&n&&mContext&/span&&span class=&o&&).&/span&&span class=&na&&inflate&/span&&span class=&o&&(...);&/span&
&span class=&n&&v&/span&&span class=&o&&.&/span&&span class=&na&&setOnclickListener&/span&&span class=&o&&(&/span&&span class=&k&&new&/span& &span class=&n&&View&/span&&span class=&o&&.&/span&&span class=&na&&OnClickListener&/span& &span class=&o&&()&/span& &span class=&o&&{&/span&
&span class=&nd&&@override&/span&
&span class=&kd&&public&/span& &span class=&kt&&void&/span& &span class=&nf&&onClick&/span&&span class=&o&&(&/span&&span class=&n&&View&/span& &span class=&n&&v&/span&&span class=&o&&)&/span& &span class=&o&&{&/span&
&span class=&n&&getItem&/span&&span class=&o&&(&/span&&span class=&n&&position&/span&&span class=&o&&);&/span&
&span class=&o&&}&/span&
&span class=&o&&});&/span&
&span class=&o&&}&/span& &span class=&k&&else&/span& &span class=&o&&{&/span&
&span class=&o&&}&/span&
&span class=&o&&}&/span&
&/code&&/pre&&/div&&br&你看下,如果绑定数据在convertView为空的情况下确实只创建了有限个listener,&br&但是在这种情况下绑定上的数据只有View创建时的7个,之后不为空的情况下没有更换listener导致重用的view数据是新的,listener里面的position依然是过去创建view时的7个之一,不会变化(注意参数上的final)&br&&br&若在else里面再重新setListener,view是有重用,listener被换成新的,并与新的position绑定,老的listener就会变成一个废对象,等待gc回收,随着list滚动,越来越多&br&&br&关键是我们的业务与position这个参数有关&br&&br&不知道这回我表述清楚了没有
有一个小细节,很多开发人员都没有注意过比如你的Item中有三个按钮,你要为三个按钮分别定义点击事件,如何定义?也许你会在getView中这样做button1.setOnclickListener(new View.OnClickListener() {
public void onClick(View v) {
//balaba…
1.&b&重用 &/b&&b&convertView &/b&&br&用以避免重复创建 View,重复创建 View 代价较大,而且如果重用 view 不改变宽高,重用View可以减少重新分配缓存造成的内存频繁分配/回收;&br&&br&&b&2. 避免在 getView 中有 重复调用的 findViewById&/b&&br&findViewById 的实现是遍历,如果你定义的 View 越复杂代价越大。&br&Google 推荐的做法是用 ViewHolder,然后保存在 view 的 tag 中。现在 RecyclerView 也是强制使用 ViewHolder 了。&br&&br&&b&3. 设置 View (如 TextView#setText )之前先对比数据是否有改变&/b&&br&一般来说,【比较两个数据的代价】远小于【 View 的重绘的代价】&br&&br&4. &b&避免在 getView 函数中直接加载 Image 或做其他比较耗时的操作&/b&&br&加载本地 Image 需要载入内存以及解析 Bitmap ,都是比较耗时的操作。&br&用户快速滑动列表时,会大量调用 getView ,而 getView 是在主线程中被调用的。如果你在 getView 函数中直接加载 Image 或做其他耗时操作,就会造成滑动比较卡。加载 ImageView 的解决方案就是开一个线程去把做这事。有很多第三库可以做这事。&br&&br&&b&5. ListView 中元素避免半透明&/b&&br&半透明绘制需要大量乘法计算,在滑动时不停重绘会造成大量的计算,在比较差的机子上会比较卡。在设计上能不半透明就不不半透明。实在要弄的话我个人是用个比较偷懒的方法,是在滑动的时候把半透明设置成不透明,滑动完再重新设置成半透明。&br&&br&&b&6. 尽量开启硬件加速&/b&&br&硬件加速提升巨大,避免使用一些不支持的函数导致含泪关闭某个地方的硬件加速。&br&当然这一条不只是对 ListView。&br&&br&&b&. 用 ListView 威力加强版 -- RecyclerView&/b&&br&更多的新武将,更多的姿势,更规范的使用,更好用的动画,更加强大的变化
1.重用 convertView 用以避免重复创建 View,重复创建 View 代价较大,而且如果重用 view 不改变宽高,重用View可以减少重新分配缓存造成的内存频繁分配/回收;2. 避免在 getView 中有 重复调用的 findViewByIdfindViewById 的实现是遍历,如果你定义的 View…
已有帐号?
无法登录?
社交帐号登录
Android软件工程师+图像处理有关ListView优化机制及滑动时数据错乱有关问题的讨论_移动开发大全_优良自学吧 |
当前位置: >
> 有关ListView优化机制及滑动时数据错乱有关问题的讨论优良自学吧提供有关ListView优化机制及滑动时数据错乱有关问题的讨论,有关ListView优化机制及滑动时数据错乱问题的讨论&&&&&& 该篇内容主要是记录我在实际开发中遇到的ListView滑动时数据错乱的几种情况,以及解决方法。在进行ListView滑动时数据错乱问题讨论之前会对ListView所谓有关ListView优化机制及滑动时数据错乱问题的讨论&&&&&& 该篇内容主要是记录我在实际开发中遇到的ListView滑动时数据错乱的几种情况,以及解决方法。在进行ListView滑动时数据错乱问题讨论之前会对ListView所谓的&优化&进行说明。文章末尾分享了几篇文章,增强对ListView使用以及Adapter优化的理解,其中有对adapter优化方法的耗时测试的介绍。
Getting Start
遇到过的ListView在滑动时数据错乱的几种情况:
Listview滑动后,图片(/背景色)重复混乱(非异步加载时)Listview选取checkbox后,再滑动时,出现checkbox选取错位问题ListView异步加载图片时,图片显示重复错乱
这里所说的&重复混乱&是指:在滑动list的时候,会看到某行本不该显示却重复显示了其他行的数据(根据情况的不同,数据可以是文本,checkbox的选中状态,图片,背景色等等...),而之所以让人感觉到混乱或者说错乱是因为这些item的重复现象有时候看似没有什么规律可寻。
在进行问题重现之前,先有必要对处理数据与视图显示的adapter类以及ViewHolder模式进行深入理解:
Adapters and Holder Pattern
adapter是数据与listview视图显示之间的桥梁:
adapter manages the data model and adapts it to the individual rows in the list view. An adapter extends theBaseAdapter class.
The adapter would inflate the layout for each row in itsgetView() method and assign the data to the individual views in the row.
The adapter is assigned to theListView via thesetAdapter method on theListView object.”
adapter中最重要的方法非getView()莫属,listview每一行的显示都会调用getView()方法,通过getView()方法将每一行要显示的数据指定给相应的view。
getView()通常的写法如下:
private class ViewHolder{
private TextView brandEnNameTv;
private TextView brandChNameTv;
private CheckBox followCheckB
public View getView(int i, View convertView, ViewGroup viewGroup) {
ViewHolder viewHolder =
if(null == convertView){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.item_testdisorderitem,null);
viewHolder = new ViewHolder();
viewHolder.brandChNameTv = (TextView) convertView.findViewById(R.id.item_chName_txt);
viewHolder.brandEnNameTv= (TextView) convertView.findViewById(R.id.item_enName_txt);
convertView.setTag(viewHolder);
viewHolder = (ViewHolder) convertView.getTag();
//set data
return convertV
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 代码片段1.1
以上这种写法listview进行了优化,对比与以下这种方式:
public View getView(int i, View view, ViewGroup viewGroup) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowview = inflater.inflate(R.layout.item_testdisorderitem,null);
TextView brandChNameTv = (TextView) rowview.findViewById(R.id.item_chName_txt);
TextView brandEnNameTv= (TextView) rowview.findViewById(R.id.item_enName_txt);
//set data
}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 代码片段1.2
具体是怎么优化的以及优化了什么呢?
从xml布局文件里inflate的每一个view都会产生一个java对象(eg:View view):
View view = inflater.inflate(R.layout.item_testdisorderitem, null);
inlating 布局文件和创建java对象对时间和内存的消耗都是昂贵的。
除此之外,使用findViewById()方法也相对地耗时。
为了让listview减少在时间和内存上的消耗,Android提供了convertView参数(getView方法的第二个参数,不一定都叫convertView,个人认为叫rowView更好)来实现这一优化。当用户滑动列表时,原先可视的item被滚出屏幕变得不可视,而代表该行的java对象可以被新的可视行复用。也就是说如果列表在手机屏幕中一屏可见的行有7行,当第一行滑出屏幕时,底部新滑出来的第8行可以复用第1行的java对象(即通过item布局inflate出来的view),Android已经把第一行的布局缓存起来,作为可以复用的rowview:
如果Android已经判定某行不可视,那么Android允许adapter的getView()方法通过convertView参数来复用相关的view。adapter可以给包含在converView中的各视图指定新的数据(给row中的各个views assign new data)这样就避免了显示一行就要inflate xml file以及创建新的java对象。
如果Android不能复用某行的话,Android System就会返回null给convertView。第一次显示列表的时候,屏幕中显示可见的列表项,这时候每行返回的convertView都是null。当向上滑动列表后,有些行被滑出屏幕,convertView(/rowView)不为空,因为有了可以复用的row。所以通过判断converView是否为空来处理何时复用,当convertView(/rowView)为空时才进行inflate
xml file and create new java object,如果不为空,直接通过convertView(/rowView)来findViewById获取row里的各个view。这时候的代码可能的样子:
public View getView(int i, View convertView, ViewGroup viewGroup) {
ViewHolder viewHolder =
if(null == convertView){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.item_testdisorderitem,null);
TextView brandChNameTv = (TextView) convertView.findViewById(R.id.item_chName_txt);
TextView brandEnNameTv= (TextView) convertView.findViewById(R.id.item_enName_txt);
//set data
return convertV
}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 代码片段1.3
之前我们说过除了inlating 布局文件和创建java对象对时间和内存的消耗都是昂贵的。使用findViewById()方法也相对地耗时。我们知道每个列表项都会调用getView方法,如果执行&代码片段1.3&那么每显示一个列表项rowView就要调用findViewById来查找各个view。多次findViewById不仅增加了时间消耗,也创建了更多的java对象,从而造成了expensive
with regards to time and memory consumption。
为了解决这个问题,引入了View Holder Pattern。
以下对ViewHolder的描述来自于该文章Using lists in Android(ListView) 8.4.节的翻译:
View Holder pattern的使用减少了adapter中对findViewById()的调用。
ViewHoler类是adapter里自定义的一个(静态)内部类,他持有布局文件中相关view的引用。该ViewHolder的引用通过setTag()方法作为一个tag被指派给row view(convertView)。
如果我们接受了一个convertview对象,我们可以通过getTag()方法获取到ViewHolder的实例,然后经由该ViewHolder的引用指定新的属性给Views。
虽然这听起来很复杂,但是使用这种方式比使用findViewById在速度上提高了大约15%。
view holder pattern的目的即为减少findViewById()的调用次数,因为findViewById()这个方法比较耗时,至于两者之间的比较可以自行测试,测试方法可以参考农民伯伯的
因此有了现在的这种模型:
private class ViewHolder{
private TextView brandEnNameTv;
private TextView brandChNameTv;
private CheckBox followCheckB
public View getView(int i, View viewrow, ViewGroup viewGroup) {
ViewHolder viewHolder =
if(null == viewrow){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
viewrow = inflater.inflate(R.layout.item_testdisorderitem,null);
viewHolder = new ViewHolder();
viewHolder.brandChNameTv = (TextView) viewrow.findViewById(R.id.item_chName_txt);
viewHolder.brandEnNameTv= (TextView) viewrow.findViewById(R.id.item_enName_txt);
viewrow.setTag(viewHolder);
viewHolder = (ViewHolder) viewrow.getTag();
BrandItemInfo brandItemInfo = (BrandItemInfo) getItem(i);
viewHolder.brandChNameTv.setText(brandItemInfo.getBrandChName());
viewHolder.brandEnNameTv.setText(brandItemInfo.getBrandEnName());
}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 代码片段1.4
当convertView为空时,创建ViewHolder,通过viewHolder.brandChNameTv = (TextView) convertView.findViewById(R.id.item_chName_txt);来持有布局文件中view的引用。通过setTag(Object)方法把convertView和viewHolder关联起来。当接收到一个converView后,通过getTag(Object)方法获取到viewHolder。这样不仅使用convertView做到了复用现有的views的目的,同时和convertView“绑定的”viewHolder也做到了被复用。
ListView 滑动时图片(或背景色)重复混乱
在讨论图片重复问题之前,先来看看背景色重复的问题:
想要达到的效果:品牌列表的前三项的背景色设置为砖红色,剩余其他项的背景色为默认色。
实际效果:滑动列表后,后面应该显示默认背景色的item也会出现砖红色背景。
背景色重复的效果图如下:
通过代码来进一步分析,下面是处理列表数据显示的adapter类名为“TestDisorderListAdapter”继承BaseAdapter。brandInfoList是一个列表,包含了一个个BrandItemInfo对象(data modle):
package com.aliao.myandroiddemo.
import android.content.C
import android.util.L
import android.view.LayoutI
import android.view.V
import android.view.ViewG
import android.widget.BaseA
import android.widget.CheckB
import android.widget.TextV
import com.aliao.myandroiddemo.R;
import com.aliao.myandroiddemo.domain.BrandItemI
import java.util.L
* Created by liaolishuang on 14-3-31.
public class TestDisorderListAdapter extends BaseAdapter{
private List&BrandItemInfo& brandInfoL
private final String TAG = &disorderlist&;
public TestDisorderListAdapter(Context context, List&BrandItemInfo& list){
this.context =
brandInfoList =
public int getCount() {
return brandInfoList.size();
public Object getItem(int i) {
return null != brandInfoList?brandInfoList.get(i):
public long getItemId(int i) {
private class ViewHolder{
private TextView brandEnNameTv;
private TextView brandChNameTv;
private CheckBox followCheckB
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder =
if(null == view){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_testdisorderitem,null);
viewHolder = new ViewHolder();
viewHolder.brandChNameTv = (TextView) view.findViewById(R.id.item_chName_txt);
viewHolder.brandEnNameTv= (TextView) view.findViewById(R.id.item_enName_txt);
view.setTag(viewHolder);
viewHolder = (ViewHolder) view.getTag();
BrandItemInfo brandItemInfo = (BrandItemInfo) getItem(i);
viewHolder.brandChNameTv.setText(brandItemInfo.getBrandChName());
viewHolder.brandEnNameTv.setText(brandItemInfo.getBrandEnName());
if(i & 3){
view.setBackgroundColor(context.getResources().getColor(R.color.coupletwo));
为了设置品牌列表的前三项的背景色,在getView()方法中加入代码:
if(i & 3){
view.setBackgroundColor(context.getResources().getColor(R.color.coupletwo));
i是代表每一行的position,由0开始。当向上滑动列表时,i的值随着滑动行的改变而递增,没有任何问题,但是为什么只有i&3的情况才改变颜色,其他项还会有背景色的改变呢?
在Adapters and Holder Pattern部分我们已经了解了listview的缓存优化机制,滚出屏幕的视图会被缓存下来并被复用。我们以为只要设置i&3就可以让前3项变色,其他项自然就是背景色,却忽略了,除了前三项之外的某些行会去复用前三项的视图因此也就会有相同的背景色。所以当i&=3对应的某些行由于复用了i&3对应的行,造成了背景色也为砖红色。
解决方法就是必须对i不小于3的情况进行处理,把它设置为背景色即:
if(i & 3){
view.setBackgroundColor(context.getResources().getColor(R.color.coupletwo));
view.setBackgroundColor(context.getResources().getColor(R.color.background));
}对于图片重复的问题,其实是一样的道理,举这么个例子:
评论列表中会显示头像和评论内容,从手机接口端读取评论内容,其中的一个字段是用户头像的下载地址,因为有的用户有上传头像有的没有,如果用户没有上传头像,那么返回的用户头像的字段为空字符串。在getView()里面就需要判断当用户头像不为空字符串的时候去设置头像,否则为默认头像,该默认头像由应用程序本地存储的图片,在xml文件的ImageView里设置了。正确的代码片段为:
&&&&&& if(!brandItemInfo.getBrandImage().equal(&&)){
&&&&&&&&&& //根据url loadImage
&&&&&& }else{
&&&&&&&&&& viewHolder.brandLogo.setImageResource(R.drawable.ic_default_head);
如果不进行else处理,那么原先显示默认头像的imageView在list滑动后会重复显示其他头像的图片。
ListView滑动后出现checkbox选取错位
我遇到过的checkbox选取错位是列表上有checkbox,选取某个checkbox为选中状态,然后滑动列表发现其他未选择过的checkbox也变成了选中状态。重现该问题只需要在item的xml file中加入checkbox,然后java代码里不对checkbox做任何处理,测试时直接选中某个checkbox,滑动列表就会看到某些行会重复出现checkbox被选中,这种情况听起来和上一节所说的listview缓存优化问题引起重复错乱如出一辙(当然这只是我重现问题最简单的方式)。但是之所以把checkbox单独拿出来说是因为他与上述的问题并不完全相同,它涉及到列表项上的某个view状态的改变会影响到与该列表项对应的数据对象的改变。滑动list后checkbox选中状态出现问题,是由于rowview和对象一一对应,对象里的状态值没有相应改变。
在列表上像checkbox、toglebutton这种控件,它的选取状态会影响到数据的变化。例如在列表项上的checkbox,如果是选中后,该列表项对应的对象里的数据就会发生改变。这就涉及到数据模型与列表项之间的&通讯&,当列表项上的checkbox状态改变,要相应地修改改行对应对象的数据。
“The row can also containviews which interact
with the underlying data model via the adapter. For example, you can have aCheckbox in your row layout and if the
Checkbox is selected, the underlying data is changed.
Frequently you need to select items in yourListView. As the row of the
ListView are getting recycled you cannot store the selection on theView level.
To persist the selection you have to update your data model with the selected state.
To update the data model in yourListView you define your own
Adapter class. In this adapter class you attach a listener to theView which is responsible for selecting the model element. If selected you update the state in the model which you can add as a tag to the View
to have access to it.”
我们在处理listview带有像checkbox,toglebutton这类控件的时候,需要监听控件的状态,一旦状态发生改变,就去改变列表项对应的对象数据。通过setTag()方法把checkbox与对象绑定在一起,一旦状态改变就在监听方法里通过getTag()方法将对象取出,更改对象中选中字段的状态值。
代码实现:
domain中的实体类BrandItemInfo类:
增加了isSelected变量用来保存checkbox的选取状态
package com.aliao.myandroiddemo.
* Created by liaolishuang on 14-3-31.
public class BrandItemInfo {
private String brandEnN
private String brandChN
private String brandI
private int brandI
public boolean isSelected() {
public void setSelected(boolean selected) {
this.selected =
public void setBrandImage(int brandImage) {
this.brandImage = brandI
public void setBrandChName(String brandChName) {
this.brandChName = brandChN
public void setBrandEnName(String brandEnName) {
this.brandEnName = brandEnN
public String getBrandChName() {
return brandChN
public String getBrandEnName() {
return brandEnN
public int getBrandImage() {
return brandI
Adapter类:
将每行的checkbox与该行相对应的brandItemInfo对象通过setTag()方法关联起来。并增加了checkbox的监听,一旦监听到选取状态的改变,就通过getTag()方法取出对象来更新brandItemInfo对象的isSelected字段的值。
package com.aliao.myandroiddemo.
import android.content.C
import android.util.L
import android.view.LayoutI
import android.view.V
import android.view.ViewG
import android.widget.BaseA
import android.widget.CheckB
import poundB
import android.widget.ImageV
import android.widget.TextV
import com.aliao.myandroiddemo.R;
import com.aliao.myandroiddemo.domain.BrandItemI
import java.util.L
* Created by liaolishuang on 14-3-31.
public class TestDisorderListAdapter extends BaseAdapter{
private List&BrandItemInfo& brandInfoL
private final String TAG = &disorderlist&;
public TestDisorderListAdapter(Context context, List&BrandItemInfo& list){
this.context =
brandInfoList =
public int getCount() {
return brandInfoList.size();
public Object getItem(int i) {
return null != brandInfoList?brandInfoList.get(i):
public long getItemId(int i) {
private class ViewHolder{
private TextView brandEnNameTv;
private TextView brandChNameTv;
private CheckBox followCheckB
private ImageView brandL
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder =
BrandItemInfo brandItemInfo = (BrandItemInfo) getItem(i);
if(null == view){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.item_testdisorderitem,null);
viewHolder = new ViewHolder();
viewHolder.brandChNameTv = (TextView) view.findViewById(R.id.item_chName_txt);
viewHolder.brandEnNameTv = (TextView) view.findViewById(R.id.item_enName_txt);
viewHolder.brandLogo = (ImageView) view.findViewById(R.id.item_brandLogo_imagev);
viewHolder.followCheckBox = (CheckBox) view.findViewById(R.id.item_follow_checkbox);
final ViewHolder finalViewHolder = viewH
viewHolder.followCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
BrandItemInfo info = (BrandItemInfo) finalViewHolder.followCheckBox.getTag();
info.setSelected(compoundButton.isChecked());
view.setTag(viewHolder);
viewHolder.followCheckBox.setTag(brandItemInfo);
viewHolder = (ViewHolder) view.getTag();
viewHolder.followCheckBox.setTag(brandItemInfo);
viewHolder.brandChNameTv.setText(brandItemInfo.getBrandChName());
viewHolder.brandEnNameTv.setText(brandItemInfo.getBrandEnName());
viewHolder.brandLogo.setImageResource(brandItemInfo.getBrandImage());
viewHolder.followCheckBox.setChecked(brandItemInfo.isSelected());
好文分享:
Lars Vogel的:
Using lists in Android(ListView) 对ListView的使用做了非常系统完整的讲解,由浅到深很适合阅读。阅读了这篇文章后,对ListView的缓存优化机制有了更进一步的理解,从而有了本篇blog的Adapter
and Holder Pattern作为阅读后的总结。
农民伯伯的:
ListView性能优化之视图缓存 介绍了Google I/O提供的优化Adapter方案,并对这些方案进行了测试。
ListView性能优化之视图缓存续 介绍了新浪微博中主界面的做法及测试数据
(本文来自互联网,不代表搜站(/)的观点和立场)本站所有内容来自互联网,若本站收录的信息无意侵犯了贵司版权,请给我们来信(),我们会及时处理和回复,谢谢编辑推荐最近更新

我要回帖

更多关于 listview性能优化 的文章

 

随机推荐