这几天一直再做Listview的优化,今天来总结一下
做的这个项目Listview比较复杂,关于Item的种类大概有8种,每个item的布局又比较复杂,item里面的控件比较多
种类比较多 我们需要 重写adapter的
@Override
public int getItemViewType(int position) {
if(position%8==0) curType = type.get(0);
.....
return curType;
}
@Override
public int getViewTypeCount() {
return type.size();
}
1.复用ConvertView, 这是最常见的一种,listview在滑动过程中,画出屏幕的item会缓存成一个convertview对象,减少创建view的次数
在getView中 判断
if (null == convertView) { convertView = View.inflate(context, R.layout.item_view, null); }
2.ViewHolder机制, 减少 findviewById的次数,特别是item种类比较多的时候效果会比较明显一点
/** * Viewholder的简化 * @ClassName: ViewHolder * @Description: TODO * @author smile * @date 2014-5-28 上午9:56:29 */ @SuppressWarnings("unchecked") public class ViewHolder { public static <T extends View> T get(View view, int id) { SparseArray<View> viewHolder = (SparseArray<View>) view.getTag(); if (viewHolder == null) { viewHolder = new SparseArray<View>(); view.setTag(viewHolder); } View childView = viewHolder.get(id); if (childView == null) { childView = view.findViewById(id); viewHolder.put(id, childView); } return (T) childView; } }
使用:
ImageView bananaView = ViewHolder.get(convertView, R.id.banana); TextView phoneView = ViewHolder.get(convertView, R.id.phone);基本上大家都会通过上面两点进行优化,下面再说一下我的其他优化
3. 监听listIvew的滑动状态与滑动速度,根据不同的状态速度来进行不同程度的加载item,减少不必要的视图更新
orgListview.setOnScrollListener(new OnScrollListener() { boolean isFling = false ; int lastidx = 0 ; @Override public void onScrollStateChanged(AbsListView view, int scrollState) { orgListview.onScrollStateChanged(view, scrollState); //判断listview状态 switch(scrollState) { case SCROLL_STATE_IDLE: break ; case SCROLL_STATE_FLING: break ; } } @Override public void onScroll(AbsListView arg0, int firstVisibleItem, int visibleItemCount, int totalItemCount) { orgListview.onScroll(arg0, firstVisibleItem, visibleItemCount, totalItemCount); //计算速度 int speed = calcSpeed(); }) ;
计算速度的(不是太精确):
protected int calcSpeed() { int top = getScrollY(); System.out.println("top="+top); long currentTime = System.nanoTime(); int dPx = Math.abs(lastTop - top); long dTime = (currentTime-lastTime); double speed2 = dPx*1.0f /dTime*1000000000; lastTime = currentTime; lastTop = top; return (int) speed2; } public int getScrollY() { View c = listview.getChildAt(0); if (c == null) { return 0; } int firstVisiblePosition = listview.getFirstVisiblePosition(); int top = c.getTop(); return -top + firstVisiblePosition * c.getHeight() ; }
4. item中的空件宽高尽量写成固定的值或者math_parent,减少view的绘制
例如,当我们设置 textview的shettext()时,如果textview的内容长度变化了,可能会影响其他控件的位置,TextView的父控件会重新绘制他的子view
5. FaceBook工程师的博客中介绍的,FaceBook的listview的item比较大,有时候甚至占到一屏幕一个item,每次滑动出现一个新的item时,需要在getview刷新一整个屏幕的数据,然而这些数据还未出现在用户面前,于是工程师将单个的item再次拆分成若干个小的部分,每一部分都是listview的一个item,使属于更加细分化,例如一个微博的item,拆分成3个item,头像昵称的一个,新闻内容的一个,评论转发的一个,这样有效减少每次渲染的数据,达到优化的目的
网上其他的总结
1..Adapter的getView方法里面convertView没有使用setTag和getTag方式;
2.在getView方法里面ViewHolder初始化后的赋值或者是多个控件的显示状态和背景的显示没有优化好,抑或是里面含有复杂的计算和耗时操作;
3.在getView方法里面 inflate的row 嵌套太深(布局过于复杂)或者是布局里面有大图片或者背景所致;
4.Adapter多余或者不合理的notifySetDataChanged;
5.listview 被多层嵌套,多次的onMessure导致卡顿,如果多层嵌套无法避免,建议把listview的高和宽设置为fill_parent. 如果是代码继承的listview,那么也请你别忘记为你的继承类添加上LayoutPrams,注意高和宽都是fill_parent的; 以往我一般都是将listview的高度设置成fill_parent,而这次我是设为wrap_content,这样做的问题在于,ListView没有取到实际的高度,他还要根据计算才能确定,而每一次计算应该会触发listview的渲染,所以就会出现getview的调用次数跟正常情况相比多了好几倍。所以在一般情况下,我建议把listiview在布局文件中的高度总是设置为:fill_parent(或者match_parent),这不仅仅是getview的调用次数问题,还涉及到布局的效率。