[关闭]
@flyouting 2014-04-08T17:03:56.000000Z 字数 4385 阅读 4758

优化ListView

如果你想让你的listview在每个设备上都表现的很顺滑,尤其是在2.3之后的系统中, 那么下边几项可以看一下:

  1. 在adpter中的getview中条件最小化
  2. 避免垃圾回收警告
  3. 滑动过程避免加载图片
  4. 设置scrollingCache和scrollingCache为false
  5. 列表项层次布局最小化
  6. 使用View Holder模式

我这里仍然有一些后续的问题,所以我将通过一些例子来解释我是如何改进性能,使得Listview及其顺滑。每一个情况都没有一个简单的解决方案,因为改进方案取决于你的实际代码和你所犯的错误。有些人可能采用viewholder即可,有些人可能需要扁平化自己的布局,我们都需要尝试一下,这里是一些我自己尝试过行之有效的方法。

1. 在adpter中的getview中条件最小化

不知道你是否放在主活动中放了很多的条件语句,如果没有最好,这里有个改善条件判断的例子:

改之前

  1. @Override
  2. public View getView(int position, View convertView, ViewGroup paramViewGroup) {
  3. Object current_event = mObjects.get(position);
  4. ViewHolder holder = null;
  5. if (convertView == null) {
  6. holder = new ViewHolder();
  7. convertView = inflater.inflate(R.layout.row_event, null);
  8. holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim);
  9. holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster);
  10. convertView.setTag(holder);
  11. } else {
  12. holder = (ViewHolder) convertView.getTag();
  13. }
  14. //如果你有条件语句如此,那会有问题的
  15. if (doesSomeComplexChecking()) {
  16. holder.ThreeDimention.setVisibility(View.VISIBLE);
  17. } else {
  18. holder.ThreeDimention.setVisibility(View.GONE);
  19. }
  20. // 这里设置布局每次都会执行,这种方式是错误的
  21. RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight);
  22. holder.EventPoster.setLayoutParams(imageParams);
  23. return convertView;
  24. }

改之后

  1. @Override
  2. public View getView(int position, View convertView, ViewGroup paramViewGroup) {
  3. Object current_event = mObjects.get(position);
  4. ViewHolder holder = null;
  5. if (convertView == null) {
  6. holder = new ViewHolder();
  7. convertView = inflater.inflate(R.layout.row_event, null);
  8. holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim);
  9. holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster);
  10. // 这样就只在第一次初始化时才执行
  11. RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight);
  12. holder.EventPoster.setLayoutParams(imageParams);
  13. convertView.setTag(holder);
  14. } else {
  15. holder = (ViewHolder) convertView.getTag();
  16. }
  17. //这种方式可以有效的避免条件判断
  18. holder.ThreeDimension.setVisibility(object.getVisibility());
  19. return convertView;
  20. }

2. 垃圾回收

当你创建了很多对象,然后又销毁他们时,会频繁的触发GC,所以这里建议不要在getView方法里创建大量的对象,一个更好的建议是指在ViewHolder里能够创建对象。如果你频繁的在Log中看到”GC has freed some memory”,那意味着你的问题很大,你可以看看以下几项:

3. 加载图片

如果你正在加载图片,你可以使用ImageLoader库,来自Google IO2013开源客户端,这个库加载图片很快,使用的是Volley加载图片,我们在滑动事件触发的时候不应该加载图片,因为在极短时间内,listview不能平滑的加载所有行,并且没有延迟。可能会加载完,但是在某些低端设备上就不行了。这里当滑动时间触发时,ImageLoader停止了图片加载队列,当滚动停止时,队列继续。

  1. listView.setOnScrollListener(new OnScrollListener() {
  2. @Override
  3. public void onScrollStateChanged(AbsListView listView, int scrollState) {
  4. // Pause disk cache access to ensure smoother scrolling
  5. if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
  6. imageLoader.stopProcessingQueue();
  7. } else {
  8. imageLoader.startProcessingQueue();
  9. }
  10. }
  11. @Override
  12. public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
  13. // TODO Auto-generated method stub
  14. }
  15. });

这里是Google IO2013 源码

4.listview的Scrolling 和 animate cache属性

滚动缓存基本上是一个绘图缓存,在Android中,你可以让一个View保存它的绘图到一个被称为绘图缓存的地方,基本上就是一个bitmap。默认情况下,绘图缓存被禁用,因为它占用内存。但是你可以让View明确的创建一个缓存。通过setDrawingCacheEnabled或通过硬件层(setLayerType)。绘图缓存使我们的动画平滑无比。

这种类型的动画也可以使用硬件加速,因为渲染系统可以利用这个位图,并作为一个纹理上传到GPU(如果使用硬件层)并做快速矩阵操作(比如改变透明度,旋转)。相比之下,做动画其实是在每一帧都在重绘。当你滑动Listview时,本质上Listview中的Item是在执行动画(向上或向下)。Listview对那些可见的子view使用绘图缓存以使其更快的执行动画。

使用绘图缓存有缺点么?当然有,它消耗内存,这就是为啥它默认是关闭的。在listview中,当你触摸列表并滑动一点时,缓存就被自动创建了,换句话说,当listview认为你将要滑动或者拖动它时,它会创建缓存来执行滑动或者拖动的动画。这些信息来自Numan Salati (http://stackoverflow.com/questions/15570041/scrollingcache)。

AnimationCache : 定义布局动画是否为子view创建绘图缓存,启用缓存会消耗更多的内存,需要更长的初始化,但是其提供了更好的性能,动画缓存是默认启用。你可以设定为FALSE,,因为它会导致调用GC。

修改前

  1. <ListView
  2. android:id="@android:id/list"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. android:cacheColorHint="#00000000"
  6. android:divider="@color/list_background_color"
  7. android:dividerHeight="0dp"
  8. android:listSelector="#00000000"
  9. android:smoothScrollbar="true"
  10. android:visibility="gone" />

修改后:

  1. <ListView
  2. android:id="@android:id/list"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. android:divider="@color/list_background_color"
  6. android:dividerHeight="0dp"
  7. android:listSelector="#00000000"
  8. android:scrollingCache="false"
  9. android:animationCache="false"
  10. android:smoothScrollbar="true"
  11. android:visibility="gone" />

5. 列表项布局层次

减少列表项的布局层次,这直接关系到测量view,绘制view的时长,否则可能会导致滑动时的卡顿。一定要减少不必要的布局。

6. viewholder模式

主要是用来避免findViewById()的耗时,这类资料很多,推荐看下这个
a) http://www.vogella.com/tutorials/AndroidListView/article.html#adapterperformance_hoder
b) http://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html

翻译:@flyouting
源地址

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注