异步加载图片(二)

前言:上篇《异步加载图片(一)》讲解了怎样实现异步加载图片,由于篇幅已经比较长就另开一篇讲解如何实现在用户滑动时停止加载图片,在停划时继续加载,这里我只贴出在上篇的基础上更改的部分,并加以讲解,对于完整的代码参照源码;

一、ImageAndTextListAdapter.java

先看完整代码,然后再讲更改的部分

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.AbsListView;
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;
	        
	        listView.setOnScrollListener(onScrollListener);
	    }
	    
		@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;
		}
	    @Override
	    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);
			
	        // 加载IMG,并设定到ImageView中
	        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;
	    }
	    
	    public void loadImage(){
			int start = listView.getFirstVisiblePosition();
			int end =listView.getLastVisiblePosition();
			if(end >= getCount()){
				end = getCount() -1;
			}
			asyncImageLoader.setLoadLimit(start, end);
			asyncImageLoader.unlock();
		}
	    
	    AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {
			
			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				switch (scrollState) {
					case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
						Log.v("msg", "SCROLL_STATE_FLING----lock");
						asyncImageLoader.lock();	
						break;
					case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
						Log.v("msg", "SCROLL停了---加载");
						loadImage();	
						//loadImage();
						break;
					case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
						Log.d("msg", "SCROLL_STATE_TOUCH_SCROLL----lock");
						asyncImageLoader.lock();
						break;
		
					default:
						break;
				}
				
			}
			
			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				// TODO Auto-generated method stub
				
			}
		};


}
这里的代码较上篇增加了一个ListView的滑动监听器(AbsListView.OnScrollListener),然后在滑动的时候调用asyncImageLoader.lock();将其锁定,也就是利用互斥将线程阻塞;在用户停划的时候,调用loadImage();函数将线程唤醒;
这里有几个函数是AsyncImageLoader类里实现的函数:lock()----实现线程阻塞;unlock()------解除线程阻塞;setLoadLimit(start, end)-----首先找到列表中显示的ITEM的上限和下限;loadImage()的功能就是先找到列表中在显示区域中的ITEM的上限POS和下限POS,然后只唤醒这几个线程,对于不在显示区域的线程仍然保持阻止;

二、AsyncImageLoader.java

先看完整代码:

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 {
	private Object lock = new Object();
	private boolean mAllowLoad = true;
	private boolean firstLoad = true;
	private int mStartLoadLimit = 0;
	private int mStopLoadLimit = 0;
	final Handler handler = new Handler();

	private HashMap<String, SoftReference<Drawable>> imageCache;

	public AsyncImageLoader() {
		imageCache = new HashMap<String, SoftReference<Drawable>>();
	}

	public void setLoadLimit(int startLoadLimit, int stopLoadLimit) {
		if (startLoadLimit > stopLoadLimit) {
			return;
		}
		mStartLoadLimit = startLoadLimit;
		mStopLoadLimit = stopLoadLimit;
	}

	public void restore() {
		mAllowLoad = true;
		firstLoad = true;
	}

	public void lock() {
		mAllowLoad = false;
		firstLoad = false;
	}

	public void unlock() {
		mAllowLoad = true;
		synchronized (lock) {
			lock.notifyAll();
		}
	}

	public Drawable loadDrawable(final Integer pos, final String imageUrl,
			 final ImageCallback imageCallback) {
		new Thread() {
			@Override
			public void run() {
				if (!mAllowLoad) {// 先阻塞线程
					synchronized (lock) {// 加锁
						try {
							lock.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
				// 如果是第一次加载XML就加载图片,对于是不是在显示范围内的加载控制放在VIEW中
				if (mAllowLoad && firstLoad) {
					LoadImg(pos, imageUrl, imageCallback);
				}
				// 对于在显示范围内的,对其加载IMG
				if (mAllowLoad && pos <= mStopLoadLimit
						&& pos >= mStartLoadLimit) {
					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() {
						if (mAllowLoad) {
							imageCallback.onImageLoad(pos, drawable);
						}else {
							imageCallback.onError(pos);
						}
					}
				});
				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() {
					if (mAllowLoad) {
						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;
	}

	// 回调函数
	public interface ImageCallback {
		// public void imageLoaded(Drawable imageDrawable,final ImageView
		// imageView);
		// public void onError(final ImageView imageView);
		public void onImageLoad(Integer t, Drawable drawable);

		public void onError(Integer t);
	}

}
1、先看关键部分loadDrawable()函数
public Drawable loadDrawable(final Integer pos, final String imageUrl,
		 final ImageCallback imageCallback) {
	new Thread() {
		@Override
		public void run() {
			if (!mAllowLoad) {// 先阻塞线程
				synchronized (lock) {// 加锁
					try {
						lock.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			// 如果是第一次加载XML就加载图片,对于是不是在显示范围内的加载控制放在VIEW中
			if (mAllowLoad && firstLoad) {
				LoadImg(pos, imageUrl, imageCallback);
			}
			// 对于在显示范围内的,对其加载IMG
			if (mAllowLoad && pos <= mStopLoadLimit
					&& pos >= mStartLoadLimit) {
				LoadImg(pos, imageUrl, imageCallback);
			}

		}
	}.start();
	return null;
}// loadDrawable---end
这里较上回的不同点在于加了锁---synchronized (lock);即如果用户不让加载的话,就利用synchronized (lock)和lock.wait()将线程阻塞,如果ITEM在加载范围内,就利用下面的LoadImg()函数进行加载;
然后剩下的几个不同点就是加载ITEM范围限制的设定和加锁解锁的函数了;

	public void setLoadLimit(int startLoadLimit, int stopLoadLimit) {//显示上下限设定
		if (startLoadLimit > stopLoadLimit) {
			return;
		}
		mStartLoadLimit = startLoadLimit;
		mStopLoadLimit = stopLoadLimit;
	}

	public void restore() {
		mAllowLoad = true;
		firstLoad = true;
	}

	public void lock() {//加锁
		mAllowLoad = false;
		firstLoad = false;
	}

	public void unlock() {//解锁
		mAllowLoad = true;
		synchronized (lock) {
			lock.notifyAll();
		}
	}
这几个函数比较好理解,就不讲了;
这里就实现了所有的功能,这里主要参考了这两篇文章,在最后给出,感谢原作者的无私奉献!

《又优化了一个Android ListView异步加载图片》:http://www.iteye.com/topic/1118828

《又优化了一个Android ListView异步加载图片(续)》:http://www.iteye.com/topic/1127914

对于作者的续篇,为了解决这里的开的线程太多的问题,将所有的线程整合成一个线程,这样一尝试,加载速度明显慢了好多,其实可以利用信号量的原理,同时开几个线程或者线程池,本人也没那个能力将其改成线程池,本想利用AsyncTask来重新实现,因为AsyncTask使用的线程池,奈何技术不到位,实现不了……,只能再等高人去优化了。

源码地址:http://download.csdn.net/detail/harvic880925/6802321

请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/17782063  ,谢谢!!!!!!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值