zoukankan      html  css  js  c++  java
  • [Android]ListView的Adapter.getView()方法中延迟加载图片的优化

    以下内容为原创,欢迎转载,转载请注明

    来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4139998.html

    举个例子吧,以好友列表为例

    ListView中每个Item表示一个好友,每个好友中都有一个头像,需要从服务端加载到本地,然后显示在item中。

    显然,启动加载图片的过程应该是在getView()方法中触发,启动一个线程,然后下载头像图片。这里使用我写的一个开源框架ImageLoaderSamplehttps://github.com/wangjiegulu/ImageLoaderSample)来加载图片,并实现内存缓存和本地缓存。

    额--这里不再介绍ImageLoaderSample的用法了,给个传送门:http://www.cnblogs.com/tiantianbyconan/p/3574131.html

    再来看看getView()方法的调用时机:

    1. Adapter调用NotifyDataChanged的时候

    2. ListView滚动时,也就是convertView不断复用的时候。

    也就是说,每当ListView滚动时,getView()方法不断被调用,图片下载的过程不断地执行(当然,ImageLoaderSample中会有缓存,但是内存缓存时有限的,如果内存缓存中找不到要显示的图片,那就需要到文件缓存中查找,需要进行io读写,这个也是相对比较耗时的),显然,这里面还有优化的余地。

    怎么去优化这里?只要让ListView滚动的时候图片显示的时候不要去进行io读写就好了,具体逻辑如下:

    -如果调用GetView方法时,ListView处于停止状态,则先去内存中查找头像图片;如果内存图片存在,则显示内存中保存好的图片;如果内存图片不存在,则继续到文件缓存中找,如果文件缓存图片存在,则显示文件缓存中的图片;如果文件缓存图片不存在,则根据url去网络下载这张图片,然后显示;

    -如果调用getView方法时,ListView处于滚动状态,则去内存中查找头像的图片;如果内存图片存在,则显示内存中保存好的图片;如果内存图片不存在,则显示一张默认的图片(省去了从文件缓存中找图片和网络中去请求图片的步骤)。

    这样的话,我们就必须要改写BaseAdapter,让它能够监测ListView的滚动状态,并在Adapter中可以获取到当前ListView的滚动状态。所以改造BaseAdapter,ABaseAdapterhttps://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/adapter/ABaseAdapter.java):

     1 package com.wangjie.androidbucket.adapter;
     2 
     3 import android.widget.*;
     4 import com.wangjie.androidbucket.adapter.listener.OnAdapterScrollListener;
     5 
     6 /**
     7  * Author: wangjie
     8  * Email: tiantian.china.2@gmail.com
     9  * Date: 12/3/14.
    10  */
    11 public abstract class ABaseAdapter extends BaseAdapter implements AbsListView.OnScrollListener {
    12     private OnAdapterScrollListener onAdapterScrollListener;
    13     /**
    14      * 当前listview是否属于滚动状态
    15      */
    16     private boolean isScrolling;
    17 
    18     public boolean isScrolling() {
    19         return isScrolling;
    20     }
    21 
    22     public void setOnAdapterScrollListener(OnAdapterScrollListener onAdapterScrollListener) {
    23         this.onAdapterScrollListener = onAdapterScrollListener;
    24     }
    25 
    26     protected ABaseAdapter(AbsListView listView) {
    27         listView.setOnScrollListener(this);
    28     }
    29 
    30     @Override
    31     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    32         if (null != onAdapterScrollListener) {
    33             onAdapterScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
    34         }
    35     }
    36 
    37     @Override
    38     public void onScrollStateChanged(AbsListView view, int scrollState) {
    39         if (null != onAdapterScrollListener) {
    40             onAdapterScrollListener.onScrollStateChanged(view, scrollState);
    41         }
    42 
    43         // 设置是否滚动的状态
    44         if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { // 不滚动状态
    45             isScrolling = false;
    46             this.notifyDataSetChanged();
    47         } else {
    48             isScrolling = true;
    49         }
    50     }
    51 }

    如上述代码所示,该Adapter实现了AbsListView.OnScrollListener,并在构造方法中给ListView绑定了OnScrollListener,在实现的onScrollStateChanged方法中获取到当前滚动状态,并且保存这个状态isScrolling,并暴露isScrolling()方法给外面。

    OnAdapterScrollListener这个接口是继承了AbsListView.OnScrollListener,因为这里在Adapter中一景设置了OnScrollListener了,所以如果在外面设置了新的OnScrollListener的话,就会失效了,所以必须提供另外一个setOnAdapterScrollListener,然后再传入一个OnScrollListener,然后在每个方法中进行回调就好了,因为考虑到以后可能会扩展其他的接口方法,所以这里新写了一个接口(为了以后扩展时原来的代码不会被影响,推荐使用OnAdapterScrollSampleListener这个实现类来代替OnAdapterScrollListener这个接口,OnAdapterScrollSampleListener这个类只是对OnAdapterScrollListener的所有方法进行了空实现)。

    然后我们编写一个MyAdapter继承ABaseAdapter,然后,在getView()方法中,需要显示头像的时候调用如下方法:

    // 如果在滚动(从内存中查找,找不到也不进行网络请求)

    ImageLoader.getInstances().displayImage(headUrl, headIv, null, R.drawable.default_head, isScrolling());

    看到木有?

    1. displayImage()方法发生了改变,多了最后一个参数isOnlyMemory这个参数,表示是否只是在内存缓存中找这张图片,如果没有就不再继续找下去了(displayImage原来的方法我还留着,所以不会影响之前的代码)。

    2. 调用了isScrolling()方法,作为参数isOnlyMemory的值,表示,如果正在滚动的话,就只在缓存中找这张图片。

    这样,运行原来的代码试试吧,是不是效率快了一些?

  • 相关阅读:
    linux命令整理
    各种提权姿势总结
    常用端口信息说明和利用
    近年来爆发的CVE漏洞编号
    一个优秀的SSH远程终端工具
    python-读写文件的方式
    kali安装ssh服务
    一套实用的渗透测试岗位面试题
    使用 python快速搭建http服务
    asciinema使用
  • 原文地址:https://www.cnblogs.com/tiantianbyconan/p/4139998.html
Copyright © 2011-2022 走看看