listView 图片加载性能优化
ListView是一种可以显示一系列项目并能进行滚动显示的View,每一行的Item可能包含复杂的结构,可能会从网络上获取icon等的一些图标信息,就现在的网络速度要想保持ListView运行的很好滚动流畅是做不到的
所以这里就需要把这些信息利用多线程实现异步加载
实现这样功能的类
[java]viewplaincopyprint?publicclassAsyncImageLoader{
privateHashMap<String,SoftReference<Drawable>>imageCache;
publicAsyncImageLoader(){
imageCache=newHashMap<String,SoftReference<Drawable>>();
}
publicDrawableloadDrawable(finalStringimageUrl,finalImageCallbackimageCallback){
if(imageCache.containsKey(imageUrl)){
SoftReference<Drawable>softReference=imageCache.get(imageUrl);
Drawabledrawable=softReference.get();
if(drawable!=null){
returndrawable;
}
}
finalHandlerhandler=newHandler(){
@Override
publicvoidhandleMessage(Messagemessage){
imageCallback.imageLoaded((Drawable)message.obj,imageUrl);
}
};
newThread(){
@Override
publicvoidrun(){
Drawabledrawable=loadImageFromUrl(imageUrl);
imageCache.put(imageUrl,newSoftReference<Drawable>(drawable));
Messagemessage=handler.obtainMessage(0,drawable);
handler.sendMessage(message);
}
}.start();
returnnull;
}
publicstaticDrawableloadImageFromUrl(Stringurl){
//...
}
publicinterfaceImageCallback{
publicvoidimageLoaded(DrawableimageDrawable,StringimageUrl);
}
}
publicclassAsyncImageLoader{
privateHashMap<String,SoftReference<Drawable>>imageCache;
publicAsyncImageLoader(){
imageCache=newHashMap<String,SoftReference<Drawable>>();
}
publicDrawableloadDrawable(finalStringimageUrl,finalImageCallbackimageCallback){
if(imageCache.containsKey(imageUrl)){
SoftReference<Drawable>softReference=imageCache.get(imageUrl);
Drawabledrawable=softReference.get();
if(drawable!=null){
returndrawable;
}
}
finalHandlerhandler=newHandler(){
@Override
publicvoidhandleMessage(Messagemessage){
imageCallback.imageLoaded((Drawable)message.obj,imageUrl);
}
};
newThread(){
@Override
publicvoidrun(){
Drawabledrawable=loadImageFromUrl(imageUrl);
imageCache.put(imageUrl,newSoftReference<Drawable>(drawable));
Messagemessage=handler.obtainMessage(0,drawable);
handler.sendMessage(message);
}
}.start();
returnnull;
}
publicstaticDrawableloadImageFromUrl(Stringurl){
//...
}
publicinterfaceImageCallback{
publicvoidimageLoaded(DrawableimageDrawable,StringimageUrl);
}
}
注意这里使用了SoftReference来缓存图片,允许GC在需要的时候可以对缓存中的图片进行清理。它这样工作:
·调用loadDrawable(ImageUrl,imageCallback),传入一个匿名实现的ImageCallback接口
·如果图片在缓存中不存在的话,图片将从单一的线程中下载并在下载结束时通过ImageCallback回调
·如果图片确实存在于缓存中,就会马上返回,不会回调ImageCallback
然后我们还可以根据09googleI/0开发者大会提到的方式来继续优化Adapter使用ViewHolder来减少一些比较费时的操作,譬如inflateXML和findViewById()等操作
[java]viewplaincopyprint?publicclassImageAndTextListAdapterextendsArrayAdapter<ImageAndText>{
privateListViewlistView;
privateAsyncImageLoaderasyncImageLoader;
publicImageAndTextListAdapter(Activityactivity,List<ImageAndText>imageAndTexts,ListViewlistView){
super(activity,0,imageAndTexts);
this.listView=listView;
asyncImageLoader=newAsyncImageLoader();
}
@Override
publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
Activityactivity=(Activity)getContext();
//InflatetheviewsfromXML
ViewrowView=convertView;
ViewCacheviewCache;
if(rowView==null){
LayoutInflaterinflater=activity.getLayoutInflater();
rowView=inflater.inflate(R.layout.image_and_text_row,null);
viewCache=newViewCache(rowView);
rowView.setTag(viewCache);
}else{
viewCache=(ViewCache)rowView.getTag();
}
ImageAndTextimageAndText=getItem(position);
//LoadtheimageandsetitontheImageView
StringimageUrl=imageAndText.getImageUrl();
ImageViewimageView=viewCache.getImageView();
imageView.setTag(imageUrl);
DrawablecachedImage=asyncImageLoader.loadDrawable(imageUrl,newImageCallback(){
publicvoidimageLoaded(DrawableimageDrawable,StringimageUrl){
ImageViewimageViewByTag=(ImageView)listView.findViewWithTag(imageUrl);
if(imageViewByTag!=null){
imageViewByTag.setImageDrawable(imageDrawable);
}
}
});
imageView.setImageDrawable(cachedImage);
//SetthetextontheTextView
TextViewtextView=viewCache.getTextView();
textView.setText(imageAndText.getText());
returnrowView;
}
}
publicclassImageAndTextListAdapterextendsArrayAdapter<ImageAndText>{
privateListViewlistView;
privateAsyncImageLoaderasyncImageLoader;
publicImageAndTextListAdapter(Activityactivity,List<ImageAndText>imageAndTexts,ListViewlistView){
super(activity,0,imageAndTexts);
this.listView=listView;
asyncImageLoader=newAsyncImageLoader();
}
@Override
publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
Activityactivity=(Activity)getContext();
//InflatetheviewsfromXML
ViewrowView=convertView;
ViewCacheviewCache;
if(rowView==null){
LayoutInflaterinflater=activity.getLayoutInflater();
rowView=inflater.inflate(R.layout.image_and_text_row,null);
viewCache=newViewCache(rowView);
rowView.setTag(viewCache);
}else{
viewCache=(ViewCache)rowView.getTag();
}
ImageAndTextimageAndText=getItem(position);
//LoadtheimageandsetitontheImageView
StringimageUrl=imageAndText.getImageUrl();
ImageViewimageView=viewCache.getImageView();
imageView.setTag(imageUrl);
DrawablecachedImage=asyncImageLoader.loadDrawable(imageUrl,newImageCallback(){
publicvoidimageLoaded(DrawableimageDrawable,StringimageUrl){
ImageViewimageViewByTag=(ImageView)listView.findViewWithTag(imageUrl);
if(imageViewByTag!=null){
imageViewByTag.setImageDrawable(imageDrawable);
}
}
});
imageView.setImageDrawable(cachedImage);
//SetthetextontheTextView
TextViewtextView=viewCache.getTextView();
textView.setText(imageAndText.getText());
returnrowView;
}
}
这里我们没有加载完iamge之后直接设定到相应的ImageView上,而是通过Tag查找,这里我们重用的View这里有个listView的引用来通过Tag查找可见CallBack的实现
[c-sharp]viewplaincopyprint?ImageViewimageViewByTag=(ImageView)listView.findViewWithTag(imageUrl);
if(imageViewByTag!=null){
imageViewByTag.setImageDrawable(imageDrawable);
}
ImageViewimageViewByTag=(ImageView)listView.findViewWithTag(imageUrl);
if(imageViewByTag!=null){
imageViewByTag.setImageDrawable(imageDrawable);
}
这里通过ViewCatch来减少了findViewById的使用
[c-sharp]viewplaincopyprint?publicclassViewCache{
privateViewbaseView;
privateTextViewtextView;
privateImageViewimageView;
publicViewCache(ViewbaseView){
this.baseView=baseView;
}
publicTextViewgetTextView(){
if(textView==null){
textView=(TextView)baseView.findViewById(R.id.text);
}
returntitleView;
}
publicImageViewgetImageView(){
if(imageView==null){
imageView=(ImageView)baseView.findViewById(R.id.image);
}
returnimageView;
}
}
publicclassViewCache{
privateViewbaseView;
privateTextViewtextView;
privateImageViewimageView;
publicViewCache(ViewbaseView){
this.baseView=baseView;
}
publicTextViewgetTextView(){
if(textView==null){
textView=(TextView)baseView.findViewById(R.id.text);
}
returntitleView;
}
publicImageViewgetImageView(){
if(imageView==null){
imageView=(ImageView)baseView.findViewById(R.id.image);
}
returnimageView;
}
}
总结:这里主要做了三点优化
在单一线程里加载图片
重用列表中行
缓存行中的View