博文出处:http://blog.csdn.net/carterjin/article/details/7995935

有时会有在加载ListView的时候,包含用户头像或其他需要到网络获取的图片信息,这时如果等待全部获取完成再显示会比较慢,很影响用户体验,所以这时就需要利用到异步加载图片的方法。

今天整理的方法,是用Thread来进行加载,没有利用ThreadPool的方法,后面的方法以后再慢慢学一下吧,先把学会的这个记下来。

具体的效果是,加入每个ListView的项只需要显示一个图片,每张图片都是本地没有的,则先把他们都显示成一张默认的图片(我用的是程序图标),然后开启后台线程去网上取图片,最后再一个一个加载出来。

下面是具体的步骤:

步骤一:写一个异步加载类,我的叫AsyncImageLoader

[java]view plaincopy
  1. package com.carter.asynchronousimage;
  2. import java.io.InputStream;
  3. import java.lang.ref.SoftReference;
  4. import java.net.URL;
  5. import java.util.HashMap;
  6. import android.graphics.drawable.BitmapDrawable;
  7. import android.graphics.drawable.Drawable;
  8. import android.net.Uri;
  9. import android.os.Handler;
  10. import android.os.Message;
  11. import android.util.Log;
  12. /**
  13. * 异步加载图片类,内部有缓存,可以通过后台线程获取网络图片。首先生成一个实例,并调用loadDrawableByTag方法来获取一个Drawable对象
  14. */
  15. public class AsyncImageLoader {
  16. /**
  17. * 使用软引用SoftReference,可以由系统在恰当的时候更容易的回收
  18. */
  19. private HashMap<String, SoftReference<Drawable>> imageCache;
  20. public AsyncImageLoader(){
  21. imageCache = new HashMap<String, SoftReference<Drawable>>();
  22. }
  23. /**
  24. * 通过传入的TagInfo来获取一个网络上的图片
  25. * @param tag TagInfo对象,保存了position、url和一个待获取的Drawable对象
  26. * @param callback ImageCallBack对象,用于在获取到图片后供调用侧进行下一步的处理
  27. * @return drawable 从网络或缓存中得到的Drawable对象,可为null,调用侧需判断
  28. */
  29. public Drawable loadDrawableByTag(final TagInfo tag, final ImageCallBack callback){
  30. Drawable drawable;
  31. /**
  32. * 先在缓存中找,如果通过URL地址可以找到,则直接返回该对象
  33. */
  34. if(imageCache.containsKey(tag.getUrl())){
  35. drawable = imageCache.get(tag.getUrl()).get();
  36. if(null!=drawable){
  37. return drawable;
  38. }
  39. }
  40. /**
  41. * 用于在获取到网络图片后,保存图片到缓存,并触发调用侧的处理
  42. */
  43. final Handler handler = new Handler(){
  44. @Override
  45. public void handleMessage(Message msg) {
  46. TagInfo info = (TagInfo)msg.obj;
  47. imageCache.put(info.url, new SoftReference<Drawable>(info.drawable));
  48. callback.obtainImage(info);
  49. super.handleMessage(msg);
  50. }
  51. };
  52. /**
  53. * 如果在缓存中没有找到,则开启一个线程来进行网络请求
  54. */
  55. new Thread(new Runnable() {
  56. @Override
  57. public void run() {
  58. TagInfo info = getDrawableIntoTag(tag);
  59. Message msg = new Message();
  60. msg.what = 0;
  61. msg.obj = info;
  62. handler.sendMessage(msg);
  63. }
  64. }).start();
  65. return null;
  66. }
  67. /**
  68. * 通过传入的TagInfo对象,利用其URL属性,到网络请求图片,获取到图片后保存在TagInfo的Drawable属性中,并返回该TagInfo
  69. * @param info TagInfo对象,需要利用里面的url属性
  70. * @return TagInfo 传入的TagInfo对象,增加了Drawable属性后返回
  71. */
  72. public TagInfo getDrawableIntoTag(TagInfo info){
  73. URL request;
  74. InputStream input;
  75. Drawable drawable = null;
  76. try{
  77. request = new URL(info.getUrl());
  78. input = (InputStream)request.getContent();
  79. drawable = Drawable.createFromStream(input, "src"); // 第二个属性可为空,为DEBUG下使用,网上的说明
  80. }
  81. catch(Exception e){
  82. e.printStackTrace();
  83. }
  84. info.drawable = drawable;
  85. return info;
  86. }
  87. /**
  88. * 获取图片的回调接口,里面的obtainImage方法在获取到图片后进行调用
  89. */
  90. interface ImageCallBack{
  91. /**
  92. * 获取到图片后在调用侧执行具体的细节
  93. * @param info TagInfo对象,传入的info经过处理,增加Drawable属性,并返回给传入者
  94. */
  95. public void obtainImage(TagInfo info);
  96. }
  97. }

里面基本每个重要的地方都在我理解的情况下加了注释,应该很多人都能看懂的。

步骤二:写一个Activity用于展示这些内容,我的是AsynImageActivity

[java]view plaincopy
  1. package com.carter.asynchronousimage;
  2. import java.util.HashMap;
  3. import java.util.List;
  4. import java.util.ArrayList;
  5. import com.carter.asynchronousimage.AsyncImageLoader.ImageCallBack;
  6. import android.app.Activity;
  7. import android.content.Context;
  8. import android.graphics.drawable.Drawable;
  9. import android.os.Bundle;
  10. import android.util.Log;
  11. import android.view.LayoutInflater;
  12. import android.view.View;
  13. import android.view.ViewGroup;
  14. import android.widget.BaseAdapter;
  15. import android.widget.ImageView;
  16. import android.widget.ListView;
  17. public class AsynImageActivity extends Activity {
  18. Context context;
  19. ListView list_lv;
  20. List<ImageEntry> mList;
  21. MyAdapter adapter;
  22. // 测试数据,网上找的图片
  23. String[] urls = new String[]{
  24. "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD9.png",
  25. "http://pic1.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD0.png",
  26. "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD7.png",
  27. "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD6.png",
  28. "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD5.png",
  29. "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD4.png",
  30. "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD3.png",
  31. "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD7.png",
  32. "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD14.png",
  33. "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD13.png",
  34. "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD12.png",
  35. "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD11.png",
  36. "http://pic2.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD5.png",
  37. "http://pic.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD1.png",
  38. "http://pic1.sc.chinaz.com/Files/pic/icons128/2328/%E7%BB%8F%E5%85%B8%E7%94%B5%E8%84%91%E6%A1%8C%E9%9D%A2%E5%9B%BE%E6%A0%87%E4%B8%8B%E8%BD%BD0.png"
  39. };
  40. /** Called when the activity is first created. */
  41. @Override
  42. public void onCreate(Bundle savedInstanceState) {
  43. super.onCreate(savedInstanceState);
  44. setContentView(R.layout.main);
  45. context = this;
  46. list_lv = (ListView) findViewById(R.id.list_lv);
  47. mList = new ArrayList<ImageEntry>();
  48. for(int i=0; i<urls.length; i++){
  49. ImageEntry entry = new ImageEntry();
  50. entry.setUrl(urls[i]);
  51. mList.add(entry);
  52. }
  53. adapter = new MyAdapter(context, mList);
  54. list_lv.setAdapter(adapter);
  55. }
  56. /**
  57. * 一个ImageEntry代表了一个带有图片地址url等其他属性的实例,为提高可扩展性,封装了一个对象。目前只包含url这一个属性。
  58. */
  59. class ImageEntry{
  60. String url;
  61. public String getUrl(){
  62. return this.url;
  63. }
  64. public void setUrl(String url){
  65. this.url = url;
  66. }
  67. }
  68. /**
  69. * 重写的Adapter
  70. */
  71. class MyAdapter extends BaseAdapter{
  72. Context context;
  73. List<ImageEntry> mList;
  74. HashMap<String, Drawable> imgCache;     // 图片缓存
  75. HashMap<Integer, TagInfo> tag_map;      // TagInfo缓存
  76. AsyncImageLoader loader;                // 异步加载图片类
  77. /**
  78. * 构造函数
  79. * @param context 上下文
  80. * @param list 包含了所有要显示的图片的ImageEntry对象的列表
  81. */
  82. public MyAdapter(Context context, List<ImageEntry> list){
  83. this.context = context;
  84. this.mList = list;
  85. imgCache = new HashMap<String, Drawable>();
  86. loader = new AsyncImageLoader();
  87. tag_map = new HashMap<Integer, TagInfo>();
  88. }
  89. @Override
  90. public int getCount() {
  91. // TODO Auto-generated method stub
  92. return mList.size();
  93. }
  94. @Override
  95. public Object getItem(int position) {
  96. // TODO Auto-generated method stub
  97. return mList.get(position);
  98. }
  99. @Override
  100. public long getItemId(int position) {
  101. // TODO Auto-generated method stub
  102. return position;
  103. }
  104. @Override
  105. public View getView(int position, View convertView, ViewGroup parent) {
  106. // TODO Auto-generated method stub
  107. ViewHolder holder = null;
  108. if(null==convertView){
  109. convertView = LayoutInflater.from(context).inflate(R.layout.adapter_item, null, false);
  110. holder = new ViewHolder();
  111. holder.img = (ImageView) convertView.findViewById(R.id.img);
  112. convertView.setTag(holder);
  113. }else{
  114. holder = (ViewHolder) convertView.getTag();
  115. }
  116. String imgurl = mList.get(position).getUrl();   // 得到该项所代表的url地址
  117. Drawable drawable = imgCache.get(imgurl);       // 先去缓存中找
  118. TagInfo tag = new TagInfo();
  119. tag.setPosition(position);  // 保存了当前在adapter中的位置
  120. tag.setUrl(imgurl);         // 保存当前项所要加载的url
  121. holder.img.setTag(tag);     // 为ImageView设置Tag,为以后再获取图片后好能找到它
  122. tag_map.put(position, tag); // 把该TagInfo对应position放入Tag缓存中
  123. if(null!=drawable){                         // 找到了直接设置为图像
  124. holder.img.setImageDrawable(drawable);
  125. }else{                                      // 没找到则开启异步线程
  126. drawable = loader.loadDrawableByTag(tag, new ImageCallBack() {
  127. @Override
  128. public void obtainImage(TagInfo ret_info) {
  129. imgCache.put(ret_info.getUrl(), ret_info.getDrawable());    // 首先把获取的图片放入到缓存中
  130. // 通过返回的TagInfo去Tag缓存中找,然后再通过找到的Tag来获取到所对应的ImageView
  131. ImageView tag_view = (ImageView) list_lv.findViewWithTag(tag_map.get(ret_info.getPosition()));
  132. Log.i("carter", "tag_view: " + tag_view + " position: " + ret_info.getPosition());
  133. if(null!=tag_view)
  134. tag_view.setImageDrawable(ret_info.getDrawable());
  135. }
  136. });
  137. if(null==drawable){ // 如果获取的图片为空,则默认显示一个图片
  138. holder.img.setImageResource(R.drawable.ic_launcher);
  139. }
  140. }
  141. return convertView;
  142. }
  143. class ViewHolder{
  144. ImageView img;
  145. }
  146. }
  147. }

在Activity中需要用到一个类叫做TagInfo,这个是作为一个Tag,保存一些例如位置、地址和图像等的信息

[java]view plaincopy
  1. package com.carter.asynchronousimage;
  2. import android.graphics.drawable.Drawable;
  3. public class TagInfo {
  4. String url;
  5. int position;
  6. Drawable drawable;
  7. public String getUrl() {
  8. return url;
  9. }
  10. public void setUrl(String url) {
  11. this.url = url;
  12. }
  13. public int getPosition() {
  14. return position;
  15. }
  16. public void setPosition(int position) {
  17. this.position = position;
  18. }
  19. public Drawable getDrawable() {
  20. return drawable;
  21. }
  22. public void setDrawable(Drawable drawable) {
  23. this.drawable = drawable;
  24. }
  25. }

步骤三:只要在Adapter的getView方法中调用loadDrawableByTag方法就可以了。

工程需要两个布局文件,一个是主界面main.xml,一个是每项所显示的布局文件adapter_item.xml

main.xml

[html]view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical" >
  6. <ListView
  7. android:id="@+id/list_lv"
  8. android:layout_width="fill_parent"
  9. android:layout_height="fill_parent"
  10. />
  11. </LinearLayout>

adapter_item.xml

[html]view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="fill_parent"
  4. android:layout_height="fill_parent"
  5. android:orientation="vertical" >
  6. <ImageView
  7. android:id="@+id/img"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. />
  11. </LinearLayout>

布局很简单,不难懂。

其他问题:

在adapter中使用了一个HashMap叫做tag_map,它的作用是保存每个position位置的TagInfo。其实这样做在显示很少项的时候还好,但是如果有几千几万个项需要显示,则会造成大量的内存浪费。

不过使用这个方法实在是逼不得已,原来的版本,是在obtainImage方法中利用url的值来找到ImageView的,例如:

[java]view plaincopy
  1. holder.img.setTag(imgurl);
  2. public void obtainImage(){
  3. // ...
  4. ImageView tag_view = (ImageView) list_lv.findViewWithTag(imgurl);
  5. // ...
  6. }

这样做有一个问题,例如我有两个项利用的是一个url,则在findViewWithTag(imgurl)时只会找到一个ImageView,则另外一个就没有处理了,所以会造成只有一个显示了正确的图片,另外一个显示默认的。

所以我进行了另外一个尝试,利用ListView的getChildAt方法得到那个位置的View,再通过getTag方法得到ViewHolder,然后再设置图片。但有时通过getChildAt()方法得到的View为null,如果不处理就崩了,就算处理了肯定也得不到预先想要的结果。查了一下网上的解释,getChildAt()方法返回的只是当前显示出来的View,对于已经隐藏掉的也就只能返回null了。所以这个方法也废了。

所以我又进行了另外一个尝试,还是利用Tag,但是利用position这个固定的值。后来还是报了NullPointerException,原因是setTag方法传入的参数是一个Object类型,把position这个int类型传入,会自动生成Integer对象。在findViewWithTag的时候,由于convertView的复用机制,有时会使原来设置该Tag的项,动态的设置成了一个新的Tag,这样就找不到了,所以返回的View也是空的。

没办法了,我只能先这样进行处理,以后再进一步解决这个问题。如果有朋友有方法,请告知,不胜感激。

Android实现异步从网络加载图片列表相关推荐

  1. Android:ViewPager详解(异步网络加载图片,带图片缓存,并带导航小圆点)

    android 应用中,如欢迎指引页面, 和图片轮播功能, 或者更多的内容在一页显示不了,要分成多个页面,这时候viewpager是很好用的. 首先看下效果: 下面是一个例子,带异步网络加载图片,并带 ...

  2. Android的轮播图Banner之本地加载和网络加载图片(含demo)

    前言 关于轮播图 我个人是比较喜欢 闲来无事的时候 可以整理自己的照片 做一个demo 看看动态的效果 挺不错的! 每个App也基本都有一些广告位置 只要打开这个页面就会无限轮播广告图片 看多了我们也 ...

  3. Android框架之路——Glide加载图片(结合RecyclerView、CardView)

    Android框架之路--Glide加载图片 一.简介: 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech.这个库被广泛的运用在google的开 ...

  4. iOS网络加载图片缓存策略之ASIDownloadCache缓存优化

    iOS网络加载图片缓存策略之ASIDownloadCache缓存优化 在我们实际工程中,很多情况需要从网络上加载图片,然后将图片在imageview中显示出来,但每次都要从网络上请求,会严重影响用户体 ...

  5. Android 二次封装网络加载框架

    Android 二次封装网络加载框架 写在最前面 开发当中,在请求网络的时候,大家或多或少都会使用一些第三方框架,Android-Async-Http. Volley.XUtils.Okhttp.Re ...

  6. Flutter Image从网络加载图片刷新、强制重新渲染

    Flutter自带的Image.network()从网络加载图片后,如果服务器上的图片改变了,但是url没变,就算使用setState进行rebuild,图片也不会跟着更新.这是因为Image自带了c ...

  7. Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址

    关注finddreams,一起分享,一起进步: http://blog.csdn.net/finddreams/article/details/44619589 今天给大家带来一点干货,就是横向循环滚 ...

  8. android访问服务器文件,访问服务器(加载图片)

    一丶一个简单的访问服务器(访问网易新闻客户端)点击加载新闻:首页的布局文件 xmlns:tools="http://schemas.android.com/tools" andro ...

  9. Android一行代码实现网络加载GIF闪图(附源码)

    最近项目有个需求是要从网络加载GIF闪图, 但是Android原生的ImageView并不支持Gif... 于是从网上看了些Dome, 发现总是有些这样那样的问题, 譬如: ☹ 没有缓存,还要自己写一 ...

最新文章

  1. java 给控件添加边框_Android UI 利用Drawable Shape给控件加边框/立体效果
  2. winrunner事务概念的代码应用(毫秒级)
  3. php mod11 10公式,AQL RQL
  4. 信息安全系统设计基础第十二周学习总结
  5. ConcurrentLinkedQueue非阻塞队列实现原理分析
  6. ExecutorCompletionService分析及使用
  7. 移动端分步注册_移动应用程序的可用性测试:分步指南
  8. HTML照片墙 个性相册源码
  9. 存储过程的优缺点_普洱紧压茶与散茶的优缺点
  10. 电商优秀设计作品展示
  11. jzxx1000~1010题分析
  12. 软件工程结对项目:四则运算web
  13. python回溯算法全排列_python 回溯法 子集树模板 系列 —— 11、全排列
  14. 蓝桥杯 算法提高 全排列 next_pertumutation
  15. STM32移植FATFS+USB+FLASH+PDFLIB库总结
  16. 在开发板显示24位的bmp格式图片
  17. 为什么买入不了创业版_为什么说不能买创业板?
  18. 修改UE4的缓存路径
  19. C语言漫画编程从未如此简单,【图片】【编程!】记录我C语言的一步步【极度恐慌漫画吧】_百度贴吧...
  20. 信息系统高级项目管理师英语词汇(二)-常见项目管理词汇

热门文章

  1. 拥有Adobe国际认证证书,能闯出一个怎样的未来?
  2. 三分钟搭建开源堡垒机JumpServer
  3. TP6 事件绑定、监听、订阅
  4. 兼职python程序员_《兼》字意思读音、组词解释及笔画数 - 新华字典 - 911查询
  5. 南邮 OJ 1668 撼地神牛
  6. Python 程序设计快速入门简易教程
  7. 经济不确定环境下,制造业的数字化转型之道
  8. Jenkins执行批处理文件、powershell失败
  9. 计算机网络设备互连与管理,软考网络管理员备考知识点精讲之计算机网络互连设备...
  10. 简单步骤:PHP实现斗地主式随机发牌