Android ListView异步加载图片方法详解
本文实例讲述了AndroidListView异步加载图片方法。分享给大家供大家参考,具体如下:
先说说这篇文章的优点把,开启线程异步加载图片,然后刷新UI显示图片,而且通过弱引用缓存网络加载的图片,节省了再次连接网络的开销。
这样做无疑是非常可取的方法,但是加载图片时仍然会感觉到轻微的卡屏现象,特别是listview里的item在进行快速滑动的时候。
我找了一下原因,可能是在listview快速滑动屏幕的时候划过的item太多而且每次调用getView方法后就会异步的在过去某个时间内用handler刷新一下UI,
如果在同一时间调用handler刷新UI次数多了就会造成这样的卡屏现象。
后来又一想,其实我们完全没有必要在listview正在滑动的时候去后台加载图片(不管这是图片是在缓存里还是在网络上),这样无疑造成了很大的资源浪费。
我们只需要在listview滑动停止之后再去加载listview里面显示的几个item里面的图片就好了。
根据以上想法,我做了一些设计改造:
1.在adapter的getview方法里面启动加载图片的thread,如果listview在滑动则wait
2.监听listview滑动停止事件,获得listview显示的item的最上面和最下面的序号,并唤醒所有加载图片的thread,判断加载图片的序号是否是在范围内,如果是则继续加载,如果不是则结束thread
部分代码如下:
@Override
publicViewgetView(intposition,ViewconvertView,ViewGroupparent)
{
if(convertView==null){
convertView=mInflater.inflate(R.layout.book_item_adapter,null);
}
BookModelmodel=mModels.get(position);
convertView.setTag(position);
ImageViewiv=(ImageView)convertView.findViewById(R.id.sItemIcon);
TextViewsItemTitle=(TextView)convertView.findViewById(R.id.sItemTitle);
TextViewsItemInfo=(TextView)convertView.findViewById(R.id.sItemInfo);
sItemTitle.setText(model.book_name);
sItemInfo.setText(model.out_book_url);
iv.setBackgroundResource(R.drawable.rc_item_bg);
syncImageLoader.loadImage(position,model.out_book_pic,imageLoadListener);
returnconvertView;
}
SyncImageLoader.OnImageLoadListenerimageLoadListener=newSyncImageLoader.OnImageLoadListener(){
@Override
publicvoidonImageLoad(Integert,Drawabledrawable){
//BookModelmodel=(BookModel)getItem(t);
Viewview=mListView.findViewWithTag(t);
if(view!=null){
ImageViewiv=(ImageView)view.findViewById(R.id.sItemIcon);
iv.setBackgroundDrawable(drawable);
}
}
@Override
publicvoidonError(Integert){
BookModelmodel=(BookModel)getItem(t);
Viewview=mListView.findViewWithTag(model);
if(view!=null){
ImageViewiv=(ImageView)view.findViewById(R.id.sItemIcon);
iv.setBackgroundResource(R.drawable.rc_item_bg);
}
}
};
publicvoidloadImage(){
intstart=mListView.getFirstVisiblePosition();
intend=mListView.getLastVisiblePosition();
if(end>=getCount()){
end=getCount()-1;
}
syncImageLoader.setLoadLimit(start,end);
syncImageLoader.unlock();
}
AbsListView.OnScrollListeneronScrollListener=newAbsListView.OnScrollListener(){
@Override
publicvoidonScrollStateChanged(AbsListViewview,intscrollState){
switch(scrollState){
caseAbsListView.OnScrollListener.SCROLL_STATE_FLING:
DebugUtil.debug("SCROLL_STATE_FLING");
syncImageLoader.lock();
break;
caseAbsListView.OnScrollListener.SCROLL_STATE_IDLE:
DebugUtil.debug("SCROLL_STATE_IDLE");
loadImage();
//loadImage();
break;
caseAbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
syncImageLoader.lock();
break;
default:
break;
}
}
@Override
publicvoidonScroll(AbsListViewview,intfirstVisibleItem,
intvisibleItemCount,inttotalItemCount){
//TODOAuto-generatedmethodstub
}
};
packagecindy.android.test.synclistview;
importjava.io.DataInputStream;
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.FileOutputStream;
importjava.io.IOException;
importjava.io.InputStream;
importjava.lang.ref.SoftReference;
importjava.net.URL;
importjava.util.HashMap;
importandroid.graphics.drawable.Drawable;
importandroid.os.Environment;
importandroid.os.Handler;
publicclassSyncImageLoader{
privateObjectlock=newObject();
privatebooleanmAllowLoad=true;
privatebooleanfirstLoad=true;
privateintmStartLoadLimit=0;
privateintmStopLoadLimit=0;
finalHandlerhandler=newHandler();
privateHashMap<String,SoftReference<Drawable>>imageCache=newHashMap<String,SoftReference<Drawable>>();
publicinterfaceOnImageLoadListener{
publicvoidonImageLoad(Integert,Drawabledrawable);
publicvoidonError(Integert);
}
publicvoidsetLoadLimit(intstartLoadLimit,intstopLoadLimit){
if(startLoadLimit>stopLoadLimit){
return;
}
mStartLoadLimit=startLoadLimit;
mStopLoadLimit=stopLoadLimit;
}
publicvoidrestore(){
mAllowLoad=true;
firstLoad=true;
}
publicvoidlock(){
mAllowLoad=false;
firstLoad=false;
}
publicvoidunlock(){
mAllowLoad=true;
synchronized(lock){
lock.notifyAll();
}
}
publicvoidloadImage(Integert,StringimageUrl,
OnImageLoadListenerlistener){
finalOnImageLoadListenermListener=listener;
finalStringmImageUrl=imageUrl;
finalIntegermt=t;
newThread(newRunnable(){
@Override
publicvoidrun(){
if(!mAllowLoad){
DebugUtil.debug("preparetoload");
synchronized(lock){
try{
lock.wait();
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
if(mAllowLoad&&firstLoad){
loadImage(mImageUrl,mt,mListener);
}
if(mAllowLoad&&mt<=mStopLoadLimit&&mt>=mStartLoadLimit){
loadImage(mImageUrl,mt,mListener);
}
}
}).start();
}
privatevoidloadImage(finalStringmImageUrl,finalIntegermt,finalOnImageLoadListenermListener){
if(imageCache.containsKey(mImageUrl)){
SoftReference<Drawable>softReference=imageCache.get(mImageUrl);
finalDrawabled=softReference.get();
if(d!=null){
handler.post(newRunnable(){
@Override
publicvoidrun(){
if(mAllowLoad){
mListener.onImageLoad(mt,d);
}
}
});
return;
}
}
try{
finalDrawabled=loadImageFromUrl(mImageUrl);
if(d!=null){
imageCache.put(mImageUrl,newSoftReference<Drawable>(d));
}
handler.post(newRunnable(){
@Override
publicvoidrun(){
if(mAllowLoad){
mListener.onImageLoad(mt,d);
}
}
});
}catch(IOExceptione){
handler.post(newRunnable(){
@Override
publicvoidrun(){
mListener.onError(mt);
}
});
e.printStackTrace();
}
}
publicstaticDrawableloadImageFromUrl(Stringurl)throwsIOException{
DebugUtil.debug(url);
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
Filef=newFile(Environment.getExternalStorageDirectory()+"/TestSyncListView/"+MD5.getMD5(url));
if(f.exists()){
FileInputStreamfis=newFileInputStream(f);
Drawabled=Drawable.createFromStream(fis,"src");
returnd;
}
URLm=newURL(url);
InputStreami=(InputStream)m.getContent();
DataInputStreamin=newDataInputStream(i);
FileOutputStreamout=newFileOutputStream(f);
byte[]buffer=newbyte[1024];
intbyteread=0;
while((byteread=in.read(buffer))!=-1){
out.write(buffer,0,byteread);
}
in.close();
out.close();
Drawabled=Drawable.createFromStream(i,"src");
returnloadImageFromUrl(url);
}else{
URLm=newURL(url);
InputStreami=(InputStream)m.getContent();
Drawabled=Drawable.createFromStream(i,"src");
returnd;
}
}
}
除了本身已有的弱引用缓存图片,我还添加了本地SD卡缓存图片(这两种缓存方法各有好处,如果图片经常变化建议内存缓存图片,如果是不经常修改的图片建议SD卡缓存)
更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android多媒体操作技巧汇总(音频,视频,录音等)》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》
希望本文所述对大家Android程序设计有所帮助。