Android实现异步从网络加载图片列表
博文出处:http://blog.csdn.net/carterjin/article/details/7995935
有时会有在加载ListView的时候,包含用户头像或其他需要到网络获取的图片信息,这时如果等待全部获取完成再显示会比较慢,很影响用户体验,所以这时就需要利用到异步加载图片的方法。
今天整理的方法,是用Thread来进行加载,没有利用ThreadPool的方法,后面的方法以后再慢慢学一下吧,先把学会的这个记下来。
具体的效果是,加入每个ListView的项只需要显示一个图片,每张图片都是本地没有的,则先把他们都显示成一张默认的图片(我用的是程序图标),然后开启后台线程去网上取图片,最后再一个一个加载出来。
下面是具体的步骤:
步骤一:写一个异步加载类,我的叫AsyncImageLoader
- package com.carter.asynchronousimage;
- import java.io.InputStream;
- import java.lang.ref.SoftReference;
- import java.net.URL;
- import java.util.HashMap;
- import android.graphics.drawable.BitmapDrawable;
- import android.graphics.drawable.Drawable;
- import android.net.Uri;
- import android.os.Handler;
- import android.os.Message;
- import android.util.Log;
- /**
- * 异步加载图片类,内部有缓存,可以通过后台线程获取网络图片。首先生成一个实例,并调用loadDrawableByTag方法来获取一个Drawable对象
- */
- public class AsyncImageLoader {
- /**
- * 使用软引用SoftReference,可以由系统在恰当的时候更容易的回收
- */
- private HashMap<String, SoftReference<Drawable>> imageCache;
- public AsyncImageLoader(){
- imageCache = new HashMap<String, SoftReference<Drawable>>();
- }
- /**
- * 通过传入的TagInfo来获取一个网络上的图片
- * @param tag TagInfo对象,保存了position、url和一个待获取的Drawable对象
- * @param callback ImageCallBack对象,用于在获取到图片后供调用侧进行下一步的处理
- * @return drawable 从网络或缓存中得到的Drawable对象,可为null,调用侧需判断
- */
- public Drawable loadDrawableByTag(final TagInfo tag, final ImageCallBack callback){
- Drawable drawable;
- /**
- * 先在缓存中找,如果通过URL地址可以找到,则直接返回该对象
- */
- if(imageCache.containsKey(tag.getUrl())){
- drawable = imageCache.get(tag.getUrl()).get();
- if(null!=drawable){
- return drawable;
- }
- }
- /**
- * 用于在获取到网络图片后,保存图片到缓存,并触发调用侧的处理
- */
- final Handler handler = new Handler(){
- @Override
- public void handleMessage(Message msg) {
- TagInfo info = (TagInfo)msg.obj;
- imageCache.put(info.url, new SoftReference<Drawable>(info.drawable));
- callback.obtainImage(info);
- super.handleMessage(msg);
- }
- };
- /**
- * 如果在缓存中没有找到,则开启一个线程来进行网络请求
- */
- new Thread(new Runnable() {
- @Override
- public void run() {
- TagInfo info = getDrawableIntoTag(tag);
- Message msg = new Message();
- msg.what = 0;
- msg.obj = info;
- handler.sendMessage(msg);
- }
- }).start();
- return null;
- }
- /**
- * 通过传入的TagInfo对象,利用其URL属性,到网络请求图片,获取到图片后保存在TagInfo的Drawable属性中,并返回该TagInfo
- * @param info TagInfo对象,需要利用里面的url属性
- * @return TagInfo 传入的TagInfo对象,增加了Drawable属性后返回
- */
- public TagInfo getDrawableIntoTag(TagInfo info){
- URL request;
- InputStream input;
- Drawable drawable = null;
- try{
- request = new URL(info.getUrl());
- input = (InputStream)request.getContent();
- drawable = Drawable.createFromStream(input, "src"); // 第二个属性可为空,为DEBUG下使用,网上的说明
- }
- catch(Exception e){
- e.printStackTrace();
- }
- info.drawable = drawable;
- return info;
- }
- /**
- * 获取图片的回调接口,里面的obtainImage方法在获取到图片后进行调用
- */
- interface ImageCallBack{
- /**
- * 获取到图片后在调用侧执行具体的细节
- * @param info TagInfo对象,传入的info经过处理,增加Drawable属性,并返回给传入者
- */
- public void obtainImage(TagInfo info);
- }
- }
里面基本每个重要的地方都在我理解的情况下加了注释,应该很多人都能看懂的。
步骤二:写一个Activity用于展示这些内容,我的是AsynImageActivity
- package com.carter.asynchronousimage;
- import java.util.HashMap;
- import java.util.List;
- import java.util.ArrayList;
- import com.carter.asynchronousimage.AsyncImageLoader.ImageCallBack;
- import android.app.Activity;
- import android.content.Context;
- import android.graphics.drawable.Drawable;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.ImageView;
- import android.widget.ListView;
- public class AsynImageActivity extends Activity {
- Context context;
- ListView list_lv;
- List<ImageEntry> mList;
- MyAdapter adapter;
- // 测试数据,网上找的图片
- String[] urls = new String[]{
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "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"
- };
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- context = this;
- list_lv = (ListView) findViewById(R.id.list_lv);
- mList = new ArrayList<ImageEntry>();
- for(int i=0; i<urls.length; i++){
- ImageEntry entry = new ImageEntry();
- entry.setUrl(urls[i]);
- mList.add(entry);
- }
- adapter = new MyAdapter(context, mList);
- list_lv.setAdapter(adapter);
- }
- /**
- * 一个ImageEntry代表了一个带有图片地址url等其他属性的实例,为提高可扩展性,封装了一个对象。目前只包含url这一个属性。
- */
- class ImageEntry{
- String url;
- public String getUrl(){
- return this.url;
- }
- public void setUrl(String url){
- this.url = url;
- }
- }
- /**
- * 重写的Adapter
- */
- class MyAdapter extends BaseAdapter{
- Context context;
- List<ImageEntry> mList;
- HashMap<String, Drawable> imgCache; // 图片缓存
- HashMap<Integer, TagInfo> tag_map; // TagInfo缓存
- AsyncImageLoader loader; // 异步加载图片类
- /**
- * 构造函数
- * @param context 上下文
- * @param list 包含了所有要显示的图片的ImageEntry对象的列表
- */
- public MyAdapter(Context context, List<ImageEntry> list){
- this.context = context;
- this.mList = list;
- imgCache = new HashMap<String, Drawable>();
- loader = new AsyncImageLoader();
- tag_map = new HashMap<Integer, TagInfo>();
- }
- @Override
- public int getCount() {
- // TODO Auto-generated method stub
- return mList.size();
- }
- @Override
- public Object getItem(int position) {
- // TODO Auto-generated method stub
- return mList.get(position);
- }
- @Override
- public long getItemId(int position) {
- // TODO Auto-generated method stub
- return position;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // TODO Auto-generated method stub
- ViewHolder holder = null;
- if(null==convertView){
- convertView = LayoutInflater.from(context).inflate(R.layout.adapter_item, null, false);
- holder = new ViewHolder();
- holder.img = (ImageView) convertView.findViewById(R.id.img);
- convertView.setTag(holder);
- }else{
- holder = (ViewHolder) convertView.getTag();
- }
- String imgurl = mList.get(position).getUrl(); // 得到该项所代表的url地址
- Drawable drawable = imgCache.get(imgurl); // 先去缓存中找
- TagInfo tag = new TagInfo();
- tag.setPosition(position); // 保存了当前在adapter中的位置
- tag.setUrl(imgurl); // 保存当前项所要加载的url
- holder.img.setTag(tag); // 为ImageView设置Tag,为以后再获取图片后好能找到它
- tag_map.put(position, tag); // 把该TagInfo对应position放入Tag缓存中
- if(null!=drawable){ // 找到了直接设置为图像
- holder.img.setImageDrawable(drawable);
- }else{ // 没找到则开启异步线程
- drawable = loader.loadDrawableByTag(tag, new ImageCallBack() {
- @Override
- public void obtainImage(TagInfo ret_info) {
- imgCache.put(ret_info.getUrl(), ret_info.getDrawable()); // 首先把获取的图片放入到缓存中
- // 通过返回的TagInfo去Tag缓存中找,然后再通过找到的Tag来获取到所对应的ImageView
- ImageView tag_view = (ImageView) list_lv.findViewWithTag(tag_map.get(ret_info.getPosition()));
- Log.i("carter", "tag_view: " + tag_view + " position: " + ret_info.getPosition());
- if(null!=tag_view)
- tag_view.setImageDrawable(ret_info.getDrawable());
- }
- });
- if(null==drawable){ // 如果获取的图片为空,则默认显示一个图片
- holder.img.setImageResource(R.drawable.ic_launcher);
- }
- }
- return convertView;
- }
- class ViewHolder{
- ImageView img;
- }
- }
- }
在Activity中需要用到一个类叫做TagInfo,这个是作为一个Tag,保存一些例如位置、地址和图像等的信息
- package com.carter.asynchronousimage;
- import android.graphics.drawable.Drawable;
- public class TagInfo {
- String url;
- int position;
- Drawable drawable;
- public String getUrl() {
- return url;
- }
- public void setUrl(String url) {
- this.url = url;
- }
- public int getPosition() {
- return position;
- }
- public void setPosition(int position) {
- this.position = position;
- }
- public Drawable getDrawable() {
- return drawable;
- }
- public void setDrawable(Drawable drawable) {
- this.drawable = drawable;
- }
- }
步骤三:只要在Adapter的getView方法中调用loadDrawableByTag方法就可以了。
工程需要两个布局文件,一个是主界面main.xml,一个是每项所显示的布局文件adapter_item.xml
main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <ListView
- android:id="@+id/list_lv"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- />
- </LinearLayout>
adapter_item.xml
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <ImageView
- android:id="@+id/img"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- </LinearLayout>
布局很简单,不难懂。
其他问题:
在adapter中使用了一个HashMap叫做tag_map,它的作用是保存每个position位置的TagInfo。其实这样做在显示很少项的时候还好,但是如果有几千几万个项需要显示,则会造成大量的内存浪费。
不过使用这个方法实在是逼不得已,原来的版本,是在obtainImage方法中利用url的值来找到ImageView的,例如:
- holder.img.setTag(imgurl);
- public void obtainImage(){
- // ...
- ImageView tag_view = (ImageView) list_lv.findViewWithTag(imgurl);
- // ...
- }
这样做有一个问题,例如我有两个项利用的是一个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实现异步从网络加载图片列表相关推荐
- Android:ViewPager详解(异步网络加载图片,带图片缓存,并带导航小圆点)
android 应用中,如欢迎指引页面, 和图片轮播功能, 或者更多的内容在一页显示不了,要分成多个页面,这时候viewpager是很好用的. 首先看下效果: 下面是一个例子,带异步网络加载图片,并带 ...
- Android的轮播图Banner之本地加载和网络加载图片(含demo)
前言 关于轮播图 我个人是比较喜欢 闲来无事的时候 可以整理自己的照片 做一个demo 看看动态的效果 挺不错的! 每个App也基本都有一些广告位置 只要打开这个页面就会无限轮播广告图片 看多了我们也 ...
- Android框架之路——Glide加载图片(结合RecyclerView、CardView)
Android框架之路--Glide加载图片 一.简介: 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech.这个库被广泛的运用在google的开 ...
- iOS网络加载图片缓存策略之ASIDownloadCache缓存优化
iOS网络加载图片缓存策略之ASIDownloadCache缓存优化 在我们实际工程中,很多情况需要从网络上加载图片,然后将图片在imageview中显示出来,但每次都要从网络上请求,会严重影响用户体 ...
- Android 二次封装网络加载框架
Android 二次封装网络加载框架 写在最前面 开发当中,在请求网络的时候,大家或多或少都会使用一些第三方框架,Android-Async-Http. Volley.XUtils.Okhttp.Re ...
- Flutter Image从网络加载图片刷新、强制重新渲染
Flutter自带的Image.network()从网络加载图片后,如果服务器上的图片改变了,但是url没变,就算使用setState进行rebuild,图片也不会跟着更新.这是因为Image自带了c ...
- Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址
关注finddreams,一起分享,一起进步: http://blog.csdn.net/finddreams/article/details/44619589 今天给大家带来一点干货,就是横向循环滚 ...
- android访问服务器文件,访问服务器(加载图片)
一丶一个简单的访问服务器(访问网易新闻客户端)点击加载新闻:首页的布局文件 xmlns:tools="http://schemas.android.com/tools" andro ...
- Android一行代码实现网络加载GIF闪图(附源码)
最近项目有个需求是要从网络加载GIF闪图, 但是Android原生的ImageView并不支持Gif... 于是从网上看了些Dome, 发现总是有些这样那样的问题, 譬如: ☹ 没有缓存,还要自己写一 ...
最新文章
- java 给控件添加边框_Android UI 利用Drawable Shape给控件加边框/立体效果
- winrunner事务概念的代码应用(毫秒级)
- php mod11 10公式,AQL RQL
- 信息安全系统设计基础第十二周学习总结
- ConcurrentLinkedQueue非阻塞队列实现原理分析
- ExecutorCompletionService分析及使用
- 移动端分步注册_移动应用程序的可用性测试:分步指南
- HTML照片墙 个性相册源码
- 存储过程的优缺点_普洱紧压茶与散茶的优缺点
- 电商优秀设计作品展示
- jzxx1000~1010题分析
- 软件工程结对项目:四则运算web
- python回溯算法全排列_python 回溯法 子集树模板 系列 —— 11、全排列
- 蓝桥杯 算法提高 全排列 next_pertumutation
- STM32移植FATFS+USB+FLASH+PDFLIB库总结
- 在开发板显示24位的bmp格式图片
- 为什么买入不了创业版_为什么说不能买创业板?
- 修改UE4的缓存路径
- C语言漫画编程从未如此简单,【图片】【编程!】记录我C语言的一步步【极度恐慌漫画吧】_百度贴吧...
- 信息系统高级项目管理师英语词汇(二)-常见项目管理词汇