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程序设计有所帮助。