Android自定义ListView与图片异步加载

来源:互联网 发布:javascript 入门 编辑:程序博客网 时间:2024/05/22 00:30

转载自:http://blog.csdn.net/aduovip/article/details/7195193


要实现复杂的ListView 功能, 就需要自定义 ListView

1. 自定义 ListView 的基本方法

2.  实现 BaseAdapter

3.  在ListView 中显示主页时间线基本信息

ListView 是怎么得到 Adapter 的数据, 在Adapter 接口中,有一个 getView() 方法, 通过该方法得到一个 View 对象,

这个View 对象就是一个条目要显示的内容, 当再显示一行时,就要再次调用 getView() 方法

ListView 的每一个Item 都是通过调用 Adapter 的getView() 方法获得View 对象, 然后把 View 对象放在Item里面, 这就是ListView 和 Adapter 的关系

1. 为了提高性能, 把已经生成的 View 对象 全都放在一个Map, 其中键是这个View 所处的位置,值就是View 对象,严格意义上就是 View 对象的缓存

 (如果每次调用getView都会生成一个新的View 对象,这样很影响性能),当然Map 还要放在List 中存储

因此每次要显示一个 Item 时,先从缓存中获取,如果缓存中有,就直接通过Map 对象的键取出对象, 如果缓存中不存在,就新生成一个View 对象

2. 关于几个方法,getCount()  方法返回Adapter 包含 item 数量,  getItem() 方法 根据位置得到相应的Item 对象,getItem()有一个参数position 既是UI界面中Item 的位置,又是View 对象在 缓存List的位置;  getItemId() 方法 根据位置获得Item 的Id, 当然Id可能用不上,很多时候就用了 positon; getView() 方法,前面已经有一些介绍,在该方法中有一个contertView 参数, 它可以提高ListView 性能,  还有一个parent 参数,它表示父控件

具体代码 参见 04_11_OAuth05 工程

--------------------------------------

图片的异步加载

1  为什么图片要异步加载

由于一张图片存储空间相对文本较大,  比如用单反相机照出来可能就非常大, 如果直接加载到手机上时间会很长,用户体验就差,所以需要异步加载

2  SoftReference 的作用

SoftReference 用来实现缓存,先看一张图

软引用SoftReference特点:当gc启动时,它会判断虚拟机的内存够不够用
如果不够使用,即使软引用指向一个对象,这个对象的内存依然被回收
也就是说,软引用保证对象能被gc回收, 但是又想通过一个软引用去指向这个对象

 在Java 中有一种垃圾收集器的机制, 垃圾收集器的作用是当一个对象不再被使用时,垃圾收集器会销毁这个对象,

并且收回该对象占用的内存空间

 一段示例 SoftReference<Object>  sr = new SoftReference<Object>(new Object());

当虚拟机内存不足时,将会回收对象,需要获取对象时,可以调用 get() 方法  Object obj = sr.get();

3  异步加载图片的方法, 就是要设计一个流程来实现图片异步加载

 要特别清楚: 缓存是用 Map 存储的,  这个Map 的值是一个SoftReference(软引用) 对象,  注意理清 缓存与SoftReference(软引用)的关系,不要混淆两者!

主要代码

public class MainActivity extends Activity {

 private AsyncImageLoader loader = new AsyncImageLoader();
  
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        loadImage("http://www.android.com/images/icon-partners.png",R.id.iv1);
        loadImage("http://www.android.com/images/icon-dev.png",R.id.iv2);
        loadImage("http://www.android.com/images/icon-market.png",R.id.iv3);
    }
    
    private void loadImage(final String url,final int id){
     // 如果缓存中存在,就会从缓存中取出图像, ImageCallback 也不会被执行
     ImageView iv = (ImageView)findViewById(id);
     CallbackImpl  callback = new CallbackImpl(iv);
     Drawable cacheImg = loader.loadDrawable(url,callback);
     if(null != cacheImg){
      iv.setImageDrawable(cacheImg);
     }
    }
}

/*** 图片的异步加载类, 这个类是示例项目中最重要的类**/
public class AsyncImageLoader {
 
 // 定义一个Map类型的  缓存对象,键是图片的url, 值是图片对象(bitmap),以SoftReference(软引用)表示
 private Map<String,SoftReference<Drawable>> imageCache = new HashMap<String,
  SoftReference<Drawable>>();

 // 实现图片异步加载
 public Drawable loadDrawable(final String imageUrl, final ImageCallback callback){
  //查询缓存,查看当前需要下载的图片是否已存在于缓存当中
  /*** 流程如下: 
   *  1.先判断图片url 在缓存中是否存在,如果存在说明曾经下载过
   *  2. 如果存在时,从缓存Map中根据 key 取出         
   *  3. 如果不存在,就要新建一个线程,执行下载过程
   */
  if(imageCache.containsKey(imageUrl)){
   SoftReference<Drawable> sf = imageCache.get(imageUrl);
   /** * sf.get()方法返回这个sf对象所指向的堆内存的那个对象
    * 如果为空,说明软引用所指向的对象已经被gc回收了,也就是没有了  */
   if(null != sf.get())
    return sf.get();
   
  }
  final Handler handler = new Handler(){
   
   //这个方法处理发送来的消息
   @Override
   public void handleMessage(Message msg){
    callback.imageLoaded((Drawable)msg.obj);
   }
  };
  new Thread(){
   public void run(){
    Drawable drawable  = loadImageFromUrl(imageUrl);
    //这步表示,下载完要把图片放到缓存里
    imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));
    //从handler 获得请求消息,实际上这里有设定的含义
    Message msg = handler.obtainMessage(0,drawable);
    // 这步用 handler 发送消息
    handler.sendMessage(msg);
   }   
  }.start();
  return null;
 }
 
 //该方法用于根据图片的Url, 从网络上 下载图片
 protected Drawable loadImageFromUrl(String imageUrl){
  try{
   return Drawable.createFromStream(new URL(imageUrl).openStream(),"src");
  }catch(Exception e){
   throw new RuntimeException(e);
  }
 }
 public interface ImageCallback{
  public void imageLoaded(Drawable imageDrawable);
 }
}

具体代码请参见 async_image 工程, 示意图如下:

为什么这样设计:  这里新创建一个线程,是一个异步操作,我们无法等线程完毕后再获取Drawable 对象, 所以线程需要有一个机制,保证下载完之后,

能得到通知,   实现机制就是用handler

为什么不能在新建线程里更新UI, 这是因为,新创建的线程跟UI 并不在同一个线程中, 要访问UI 的组件必须在UI 线程中, 而handler 是跟UI 同一个线程中.


0 0
原创粉丝点击