异步加载图片(一)

前言:本以为异步加载挺简单,因为网上代码多,但真想要做好,还真不那么简单,从看代码到弄懂再到自己写,实在是有太多的东西需要学了,用了两天的时间,终于弄出来了,因为用到回调函数,所以理解起来可能难度有点大,讲起来也不太好讲,我尽力讲的明白些,其实还是要多看代码,自己摸索摸索,动手写写就什么都理解了。这篇我们只讲怎样实现异步加载,对于滑动时停止加载的事下篇再讲。

实现效果:

1、异步加载图片,在加载图片时,先加载一个默认的图片,然后在后台加载图片,加载完成后显示出来;

2、当用户在滑动时,停止加载图片的线程,当停止滑动时,看哪几个ITEM在显示屏内,只加载这几个,其它线程保持阻止;(下篇再讲)

效果图:

          刚开始加载时                          向下划动(新划出来的是空白块)           停划,加载完成

    

一、XML

1、main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <ListView android:id="@+id/list" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" />
</LinearLayout>

2、列表子项XML(book_item_adapter.xml)

<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="70.0dip"
    android:background="@drawable/item"
    android:drawingCacheQuality="high"
    android:minHeight="70.0dip"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/sItemIcon"
        android:layout_width="42.0dip"
        android:layout_height="54.0dip"
        android:layout_margin="10.0dip"
        android:background="@drawable/rc_item_bg"
        android:padding="2.0dip"
        android:scaleType="fitXY" />

    <TextView
        android:text="斗破苍穹"
        android:id="@+id/sItemTitle"
        android:layout_width="fill_parent"
        android:layout_height="30.0dip"
        android:layout_alignTop="@+id/sItemIcon"
        android:layout_toRightOf="@+id/sItemIcon"
        android:gravity="center_vertical"
        android:singleLine="true"
        android:textColor="#ffffff"
        android:textSize="18.0sp" />
</RelativeLayout>

二、JAVA代码

1、主页面代码(AsyncListImage.java)

package cn.wangmeng.test;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;

public class AsyncListImage extends Activity {
	private ListView list;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        list=(ListView)findViewById(R.id.list);
        List<ImageAndText> dataArray=new ArrayList<ImageAndText>();
        for (int i = 0; i < 100; i++) {
        	 ImageAndText test=new ImageAndText("http://www.wangmeng.cn/images/logo.gif", "test");
             ImageAndText test1=new ImageAndText("http://www.pfwx.com/files/article/image/0/54/54s.jpg", "test1");
             ImageAndText test2=new ImageAndText("http://www.pfwx.com/files/article/image/0/4/4s.jpg","test2");
             ImageAndText test3=new ImageAndText("http://www.pfwx.com/files/article/image/9/9760/9760s.jpg","test3");
             ImageAndText test4=new ImageAndText("http://www.pfwx.com/files/article/image/3/3382/3382s.jpg","test4");
             ImageAndText test5=new ImageAndText("http://www.pfwx.com/files/article/image/3/3237/3237s.jpg","test5");
             dataArray.add(test);
             dataArray.add(test1);
             dataArray.add(test2);	
             dataArray.add(test3);	
             dataArray.add(test4);	
             dataArray.add(test5);	
             
		}
       
        ImageAndTextListAdapter adapter=new ImageAndTextListAdapter(this, dataArray, list);
        list.setAdapter(adapter);
        
    }
}

2、ImageAndText.java

package cn.wangmeng.test;

public class ImageAndText {
	    private String imageUrl;
	    private String text;

	    public ImageAndText(String imageUrl, String text) {
	        this.imageUrl = imageUrl;
	        this.text = text;
	    }
	    public String getImageUrl() {
	        return imageUrl;
	    }
	    public String getText() {
	        return text;
	    }
}
上面两个代码一块讲
1、ImageAndText类是用来存储要与XML绑定的图片地址和名字地址的。

2、将所有的地址都放在一个List里面(dataArray),然后将其传入ImageAndTextListAdapter()函数中;可见这个ImageAndTextListAdapter()函数是根据传进去的dataArray生成对应的Adapter的

3、然后将ImageAndTextListAdapter()返回的Adapter与listView绑定

3、ImageAndTextListAdapter.java

这是重写于baseAdapter的类,由于重写于baseAdapter,所以有几个必须重写的函数,getCount()、getItem()、getItemId()、getView(),我们先把总体代码写出来,只讲一个getView()函数,其实函数就不讲了,先着重说下getView()函数在什么时候被系统调用:

getView()函数在什么时候被系统调用:

注意一点是android系统在显示列表时,并不是把所有代表都显示出来,让你随便划,划到哪是哪;而是根据当前的在划到的ITEM,调用当前ITEM的getView()来显示它。

全部代码:

package cn.wangmeng.test;

import java.util.ArrayList;
import java.util.List;

import cn.wangmeng.test.AsyncImageLoader.ImageCallback;

import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class ImageAndTextListAdapter extends BaseAdapter {

		private LayoutInflater inflater;
	    private ListView listView;
	    private AsyncImageLoader asyncImageLoader;

	    private List<ImageAndText> dataArray=new ArrayList<ImageAndText>();

	    public ImageAndTextListAdapter(Activity activity, List<ImageAndText> imageAndTexts, ListView listView) {

	        this.listView = listView;
	        asyncImageLoader = new AsyncImageLoader();
	        inflater = activity.getLayoutInflater();
	        dataArray=imageAndTexts;
	    }
	    
		@Override
		public int getCount() {
			// TODO Auto-generated method stub
			return dataArray.size();
		}
		@Override
		public Object getItem(int position) {
			// TODO Auto-generated method stub
			if(position >= getCount()){
				return null;
			}
			return dataArray.get(position);
		}
		@Override
		public long getItemId(int position) {
			// TODO Auto-generated method stub
			return position;
		}

	    //不需要ViewHolder版,直接将ImageAndText与XML资源关联起来
	    public View getView(int position, View convertView, ViewGroup parent) {
	    	if (convertView == null) {
	        	 convertView = inflater.inflate(R.layout.book_item_adapter, null);
	        }
	        convertView.setTag(position);
	        
	        ImageAndText imageAndText = (ImageAndText) getItem(position);
	        String imageUrl = imageAndText.getImageUrl();
	        
	        TextView textView =  (TextView) convertView.findViewById(R.id.sItemTitle); 
	        // 将XML视图项与用户输入的URL和文本在绑定
	        textView.setText(imageAndText.getText());//加载TEXT
	        ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon);
	        iv.setBackgroundResource(R.drawable.rc_item_bg);//在初始化时,先把背景图片设置成默认背景,
	        							//否则在下拉时会随机匹配背景,不美观
	
	        asyncImageLoader.loadDrawable(position,imageUrl, new ImageCallback() {
	        	@Override
	    		public void onImageLoad(Integer pos, Drawable drawable) {
	    			View view = listView.findViewWithTag(pos);
	    			if(view != null){
	    				ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
	    				iv.setBackgroundDrawable(drawable);
	    			}
	    		}
	        	//加载不成功的图片处理	
				@Override
				public void onError(Integer pos) {
					View view = listView.findViewWithTag(pos);
					if(view != null){
						ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
						iv.setBackgroundResource(R.drawable.rc_item_bg);
					}
				}
	        	
	        });
	        return convertView;
	    }
}
我们着重看getView()函数
1、看这段:

if (convertView == null) {
	 convertView = inflater.inflate(R.layout.book_item_adapter, null);
}
convertView.setTag(position);

ImageAndText imageAndText = (ImageAndText) getItem(position);
String imageUrl = imageAndText.getImageUrl();

TextView textView =  (TextView) convertView.findViewById(R.id.sItemTitle); 
// 将XML视图项与用户输入的URL和文本在绑定
textView.setText(imageAndText.getText());//加载TEXT
ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon);
iv.setBackgroundResource(R.drawable.rc_item_bg);
这段代码没什么特别的就是将前面dataArray的信息与XML的元素项对应起来,并绑定,最关键的是下面这段,下面这个方法才是实现异步加载图片的关键:
asyncImageLoader.loadDrawable(position,imageUrl, new ImageCallback() {
	@Override
	public void onImageLoad(Integer pos, Drawable drawable) {
		View view = listView.findViewWithTag(pos);
		if(view != null){
			ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
			iv.setBackgroundDrawable(drawable);
		}
	}
	//加载不成功的图片处理	
	@Override
	public void onError(Integer pos) {
		View view = listView.findViewWithTag(pos);
		if(view != null){
			ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
			iv.setBackgroundResource(R.drawable.rc_item_bg);
		}
	}
	
});
这段代码的奇特之处在于,利用AsyncImageLoader类的实例(asyncImageLoader),调用方法loadDrawable()方法,就实现了加载后图像的绑定;好神奇,仔细看他是怎么做到的。这里先注意的有两点:
(1)、传进去的参数,当前项的位置(position),当前图片的URL(imageUrl),一个名称为ImageCallback()接口函数;

(2)、ImageCallback()接口函数里的两个被重写的函数onImageLoad()和onError()

3、AsyncImageLoader.java

从上面的讲解也应该能猜到这个类,主要的功能就是加载图片,然后成功后更新UI;

先看全部代码,然后再逐步讲

package cn.wangmeng.test;

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.HashMap;

import android.graphics.drawable.Drawable;
import android.os.Handler;

public class AsyncImageLoader {
	final Handler handler = new Handler();
	private HashMap<String, SoftReference<Drawable>> imageCache;
	public AsyncImageLoader() {
		imageCache = new HashMap<String, SoftReference<Drawable>>();//图片缓存
	}
	
	// 回调函数
	public interface ImageCallback {
		public void onImageLoad(Integer t, Drawable drawable);
		public void onError(Integer t);
	}

	public Drawable loadDrawable(final Integer pos, final String imageUrl,
			 final ImageCallback imageCallback) {
		new Thread() {
			@Override
			public void run() {
				
				LoadImg(pos, imageUrl, imageCallback);

			}
		}.start();
		return null;
	}// loadDrawable---end

	public void LoadImg(final Integer pos, final String imageUrl,
			final ImageCallback imageCallback) {
		// 首先判断是否在缓存中
		// 但有个问题是:ImageCache可能会越来越大,以至用户内存用光,所以要用SoftReference(弱引用)来实现
		if (imageCache.containsKey(imageUrl)) {
			SoftReference<Drawable> softReference = imageCache.get(imageUrl);
			final Drawable drawable = softReference.get();
			if (drawable != null) {
				handler.post(new Runnable() {
					@Override
					public void run() {
						imageCallback.onImageLoad(pos, drawable);
					}
				});
				return;
			}
		}
		// 尝试从URL中加载
		try {
			final Drawable drawable = loadImageFromUrl(imageUrl);
			if (drawable != null) {
				imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));
			}
			handler.post(new Runnable() {
				@Override
				public void run() {
						imageCallback.onImageLoad(pos, drawable);
				}
			});
		} catch (IOException e) {
			handler.post(new Runnable() {
				@Override
				public void run() {
					imageCallback.onError(pos);
				}
			});
			e.printStackTrace();
		}

	}

	// 根据URL加载图片,如果出现错误throws IOException式的错误,以便在LoadImg中捕获,执行OnError()函数
	public static Drawable loadImageFromUrl(String url) throws IOException {
		URL m;
		InputStream i = null;
		m = new URL(url);
		i = (InputStream) m.getContent();
		Drawable d = Drawable.createFromStream(i, "src");
		return d;
	}

}
分别讲解
1、先看这段代码

	// 回调函数
	public interface ImageCallback {
		public void onImageLoad(Integer t, Drawable drawable);
		public void onError(Integer t);
	}

	public Drawable loadDrawable(final Integer pos, final String imageUrl,
			 final ImageCallback imageCallback) {
		new Thread() {
			@Override
			public void run() {
				
				LoadImg(pos, imageUrl, imageCallback);

			}
		}.start();
		return null;
	}// loadDrawable---end
(1)首先注意,刚才我们调用的loadDrawable()函数,里面初始化并运行了一个线程,而这个线程的里面只有一个函数LoadImg(),对于这个函数下面我们具体讲,它的主要功能就是加载图片,然后更新UI
(2)上面也看出了ImageCallback是一个接口,而里面的两个函数onImageLoad()和onError()在这里是没有具体实现的,那在哪里实现呢,当然是我们上面的ImageAndTextListAdapter.java里面了,等下我们具体会再讲。

再往下看

	// 根据URL加载图片,如果出现错误throws IOException式的错误,以便在LoadImg中捕获,执行OnError()函数
		public static Drawable loadImageFromUrl(String url) throws IOException {
			URL m;
			InputStream i = null;
			m = new URL(url);
			i = (InputStream) m.getContent();
			Drawable d = Drawable.createFromStream(i, "src");
			return d;
		}
		
	public void LoadImg(final Integer pos, final String imageUrl,
			final ImageCallback imageCallback) {
		// 首先判断是否在缓存中
		// 但有个问题是:ImageCache可能会越来越大,以至用户内存用光,所以要用SoftReference(弱引用)来实现
		if (imageCache.containsKey(imageUrl)) {
			SoftReference<Drawable> softReference = imageCache.get(imageUrl);
			final Drawable drawable = softReference.get();
			if (drawable != null) {
				handler.post(new Runnable() {
					@Override
					public void run() {
						imageCallback.onImageLoad(pos, drawable);
					}
				});
				return;
			}
		}
		// 尝试从URL中加载
		try {
			final Drawable drawable = loadImageFromUrl(imageUrl);
			if (drawable != null) {
				imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));
			}
			handler.post(new Runnable() {
				@Override
				public void run() {
						imageCallback.onImageLoad(pos, drawable);
				}
			});
		} catch (IOException e) {
			handler.post(new Runnable() {
				@Override
				public void run() {
					imageCallback.onError(pos);
				}
			});
			e.printStackTrace();
		}

	}
(1)、loadImageFromUrl()函数,就是根据URL到网上加载图片,然后返回图片流Drawable类型变量
(2)、对于LoadImg()我们再拆一下,先看如何在缓存中加载

		if (imageCache.containsKey(imageUrl)) {
			SoftReference<Drawable> softReference = imageCache.get(imageUrl);
			final Drawable drawable = softReference.get();
			if (drawable != null) {
				handler.post(new Runnable() {
					@Override
					public void run() {
						imageCallback.onImageLoad(pos, drawable);
					}
				});
				return;
			}
		}
注意:

1、在这里就已经得到了图像

			SoftReference<Drawable> softReference = imageCache.get(imageUrl);
			final Drawable drawable = softReference.get();
2、得到图像之后就到了这段代码:

			if (drawable != null) {
				handler.post(new Runnable() {
					@Override
					public void run() {
						imageCallback.onImageLoad(pos, drawable);
					}
				});
				return;
			}
当我们得到图像之后,调用imageCallback.onImageLoad(pos, drawable);来更新UI,由于我们再回来看看ImageAndTextListAdapter.java中的代码

asyncImageLoader.loadDrawable(position,imageUrl, new ImageCallback() {
	@Override
	public void onImageLoad(Integer pos, Drawable drawable) {
		View view = listView.findViewWithTag(pos);
		if(view != null){
			ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
			iv.setBackgroundDrawable(drawable);
		}
	}
	
});
看到了吧,就是把得到的drawable与加载到UI中!!!!这就实现了回调

OK,就到这吧,OnError()的原理一样,只不过是对程序没有加载到图片时应该怎么处理,其实就是当没有加载到图片时就是默认图片代替。

下面给出源码:http://download.csdn.net/detail/harvic880925/6802241(不要分,仅供分享)


请大家尊重作者板权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/17766027 ,谢谢!

  • 11
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值