简单的ListView中item图片异步加载
前言:
在android开发当中,从目标地址获取图片往往都是采用异步加载的方法。当完全加载完图片后在进行显示,也有些是直接将加载的图片一点一点的显示出来。
这两个区别只是对流的处理不同而已。现在就讲讲当图片被完全获取到后在显示的方法。
一,效果图:
初始化: 获取后:
1.1,效果思路:
初始化的时候默认ImageView显示一张白色的图片,在加载完图片以后,用心的图片将白色图片给替换掉,就达到效果了。
二,代码实现:
主要类介绍:
AsyncImageLoadAdapter :ListView的自定义Adapter
AsyncLoad :图片异步加载类
MainActivity :主Activity。
相关的布局文件就不介绍了,在后面看就行了。
MainActivity:
1 public class MainActivity extends Activity { 2 3 private ListView listView; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.activity_main); 9 initView(); 10 initData(); 11 } 12 13 private void initView() { 14 listView = (ListView) findView(R.id.listView); 15 } 16 17 private void initData() { 18 List<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>(); 19 HashMap<String, Object> map1 = new HashMap<String, Object>(); 20 map1.put("img", 21 "http://img5.imgtn.bdimg.com/it/u=747474479,3247936386&fm=21&gp=0.jpg"); 22 map1.put("txt", "条目一"); 23 HashMap<String, Object> map2 = new HashMap<String, Object>(); 24 map2.put("img", 25 "http://pic.nipic.com/2007-12-22/2007122215556437_2.jpg"); 26 map2.put("txt", "条目二"); 27 HashMap<String, Object> map3 = new HashMap<String, Object>(); 28 map3.put("img", 29 "http://img1.imgtn.bdimg.com/it/u=1774561363,2410491846&fm=21&gp=0.jpg"); 30 map3.put("txt", "条目三"); 31 HashMap<String, Object> map4 = new HashMap<String, Object>(); 32 map4.put("img", 33 "http://img5.imgtn.bdimg.com/it/u=112049007,3368205326&fm=21&gp=0.jpg"); 34 map4.put("txt", "条目四"); 35 HashMap<String, Object> map5 = new HashMap<String, Object>(); 36 map5.put("img", 37 "http://img3.imgtn.bdimg.com/it/u=2953608063,4260501712&fm=21&gp=0.jpg"); 38 map5.put("txt", "条目五"); 39 list.add(map1); 40 list.add(map2); 41 list.add(map3); 42 list.add(map4); 43 list.add(map5); 44 AsyncImageLoadAdapter adapter = new AsyncImageLoadAdapter( 45 MainActivity.this, list, listView); 46 listView.setAdapter(adapter); 47 } 48 49 private <T> T findView(int id) { 50 return (T) findViewById(id); 51 } 52 53 }
这个MainActivity挺简单的,就是初始化相关控件,然后给ListView附上值而已。
AsyncImageLoadAdapter :
1 public class AsyncImageLoadAdapter extends BaseAdapter { 2 3 private Context context; 4 5 private List<HashMap<String, Object>> listData; 6 7 private AsyncLoad asyncLoad; 8 9 private ListView listView; 10 11 public AsyncImageLoadAdapter(Context context1, 12 List<HashMap<String, Object>> listData1, ListView listView1) { 13 this.context = context1; 14 this.listData = listData1; 15 this.asyncLoad = AsyncLoad.instance(); 16 this.listView = listView1; 17 } 18 19 @Override 20 public int getCount() { 21 return listData.size(); 22 } 23 24 @Override 25 public Object getItem(int position) { 26 return listData.get(position); 27 } 28 29 @Override 30 public long getItemId(int position) { 31 return position; 32 } 33 34 @Override 35 public View getView(int position, View convertView, ViewGroup parent) { 36 37 if (convertView == null) { 38 convertView = LayoutInflater.from(context).inflate( 39 R.layout.list_item, null); 40 } 41 42 convertView.setTag(position); 43 ImageView imageView = (ImageView) convertView 44 .findViewById(R.id.list_item_img); 45 imageView.setBackgroundResource(R.drawable.img_black); 46 TextView textView = (TextView) convertView 47 .findViewById(R.id.list_item_tv); 48 textView.setText("我是条目" + position); 49 HashMap<String, Object> item = listData.get(position); 50 String imageStr = (String) item.get("img"); 51 asyncLoad.loadImage(position, imageStr, new ILoadListener() { 52 53 @SuppressLint("NewApi") 54 @Override 55 public void onSuccess(Integer pos, Drawable drawable) { 56 View view = listView.findViewWithTag(pos); 57 58 if (view != null) { 59 ImageView img = (ImageView) view 60 .findViewById(R.id.list_item_img); 61 62 img.setBackground(drawable); 63 } 64 } 65 66 @Override 67 public void onFail(String failStr) { 68 Toast.makeText(context, "错误信息是->" + failStr, Toast.LENGTH_SHORT) 69 .show(); 70 } 71 }); 72 73 return convertView; 74 } 75 }
这个Adapter中加载了一个list_item布局(adapter中未用ViewHolder,不要去纠结这个,加上也可),来初始化未加载图片的时候。主要要说的是这段代码
1 asyncLoad.loadImage(position, imageStr, new ILoadListener() { 2 @SuppressLint("NewApi") 3 @Override 4 public void onSuccess(Integer pos, Drawable drawable) { 5 View view = listView.findViewWithTag(pos); 6 if (view != null) { 7 ImageView img = (ImageView) view 8 .findViewById(R.id.list_item_img); 9 img.setBackground(drawable); 10 } 11 } 12 @Override 13 public void onFail(String failStr) { 14 Toast.makeText(context, "错误信息是->" + failStr, Toast.LENGTH_SHORT) 15 .show(); 16 } 17 });
这段代码就进行了异步去加载图片。
思路是:先将ListView中的每个item设置一个Tag,便于后面找到对应的item中imageView进行图片更新。
然后利用图片加载类去网络获取图片,获取到图片后采用接口回调的方式传回图片进行ImageView更新(UI线程中
执行的)
AsyncLoad:
1 public class AsyncLoad { 2 3 private static AsyncLoad asyncLoad; 4 /** 5 * 图片缓存容器,采用软引用,当用户手机内存不够时候,系统自动回收其所占有的内存 6 */ 7 private HashMap<String, SoftReference<Drawable>> dataMap; 8 /** 9 * 用于在UI线程中进行UI更新 10 */ 11 private Handler handler = new Handler(); 12 13 public synchronized static AsyncLoad instance() { 14 15 if (asyncLoad == null) 16 asyncLoad = new AsyncLoad(); 17 return asyncLoad; 18 19 } 20 21 public AsyncLoad() { 22 dataMap = new HashMap<String, SoftReference<Drawable>>(); 23 } 24 25 // 内部接口对图片获取的监听 26 interface ILoadListener { 27 /** 28 * 图片加载成功后 29 * 30 * @param pos 31 * 对listView中pos位置 32 * @param drawable 33 * 网络加载后获取到的图片 34 */ 35 public void onSuccess(Integer pos, Drawable drawable); 36 37 public void onFail(String failStr); 38 39 } 40 41 /** 42 * 开启线程对图片进行加载 43 * 44 * @param pos 45 * listView的pos位置进行加载 46 * @param imageStr 47 * 图片地址 48 * @param listener 49 * 监听接口 50 */ 51 52 public void loadImage(final Integer pos, final String imageStr, 53 final ILoadListener listener) { 54 55 // 开启一个线程获取图片资源 56 new Thread(new Runnable() { 57 58 @Override 59 public void run() { 60 try { 61 Thread.sleep(5000); 62 } catch (InterruptedException e) { 63 e.printStackTrace(); 64 } 65 load(pos, imageStr, listener); 66 } 67 }).start(); 68 69 } 70 71 private void load(final Integer pos, final String imageStr, 72 final ILoadListener listener) { 73 74 if (dataMap.containsKey(imageStr)) { 75 76 SoftReference<Drawable> softReference = dataMap.get(imageStr); 77 final Drawable d = softReference.get(); 78 if (d != null) { 79 handler.post(new Runnable() { 80 81 @Override 82 public void run() { 83 84 listener.onSuccess(pos, d); 85 } 86 }); 87 } 88 return; 89 90 } 91 92 try { 93 final Drawable d = loadImgFromUrl(imageStr); 94 if (d != null) { 95 SoftReference<Drawable> soft = new SoftReference<Drawable>(d); 96 dataMap.put(imageStr, soft); 97 } 98 handler.post(new Runnable() { 99 100 @Override 101 public void run() { 102 103 listener.onSuccess(pos, d); 104 105 } 106 }); 107 108 } catch (final IOException e) { 109 e.printStackTrace(); 110 // 进行错误信息回报 111 handler.post(new Runnable() { 112 113 @Override 114 public void run() { 115 116 listener.onFail(e.getMessage()); 117 118 } 119 }); 120 121 } 122 123 } 124 125 /** 126 * 获取网路图片资源 127 * 128 * @param url 129 * @return 130 * @throws IOException 131 */ 132 private static Drawable loadImgFromUrl(String urlStr) throws IOException { 133 System.out.println("urlStr->" + urlStr); 134 URL url; 135 InputStream i = null; 136 url = new URL(urlStr); 137 i = (InputStream) url.getContent(); 138 Drawable drawable = Drawable.createFromStream(i, "img"); 139 140 return drawable; 141 142 } 143 144 }
要想更新界面上内容,就必须在UI线程中去更新,所以就采用handler.post方法,post里面Runnable中
run执行的代码其实是在UI线程中执行的,所以就符合android的规矩,才能进行更新
参考文章:http://blog.csdn.net/harvic880925/article/details/17766027#t7
源码下载:下载