这回先讲一下ListView异步加载图片的问题,相关的文章很多,不过在这里我将加载、压缩下载、存储到SD卡等功能全部放上来,方便大家使用或者研究。
1. ListView中的SimpleAdapter中的操作
大家在使用ListView显示复杂的页面时,我们都全重写一个SimpleAdapter,重写其中的getView方法来显示具体内容。相关的内容我们下篇文章再说,这里主要看下图片显示的代码。
01 |
//图片内容显示 |
02 |
if (!topic.getImage_value().equals( "" )){ |
03 |
vh.tl_ll_content_image.setVisibility(View.VISIBLE); |
04 |
//加上标识,要不图片加载会变乱 |
05 |
vh.tl_img_content_image.setTag(topic.getTid()); |
06 |
vh.tl_img_content_image.setImageResource(R.drawable.default_image_load); |
07 |
//新开异步加载图片 |
08 |
LoadingTopicImageAsyncTaskloadingTopicImageAsyncTask = new LoadingTopicImageAsyncTask(vh.tl_img_content_image, topic,ctxContext, true ,String.valueOf(topic.getTid())); |
09 |
loadingTopicImageAsyncTask.execute(); |
10 |
} |
·vh. tl_ll_content_image是我们在ListView中要显示相关图片的控件。Topic是我们通过getView取到的当前行的数据对象。Topic.getImage_value()中则是要显示的图片url地址,比如:http://www.huoban168.com/huoban/upload/topic/2012/02/weibo_1329196372_1112447.png。
·为图片控件加上setTag主要是防止图片混乱。
·LoadingTopicImageAsyncTask是我们新开一个异步,下载或者加载相关的图片的类。基本上主要的操作都在此类中进行。
2. LoadingTopicImageAsyncTask类中的操作
001 |
packagehb.hbwb.asynctask; |
002 |
importhb.hbwb.model.beans.Topic; |
003 |
importhb.hbwb.tools.PicTool; |
004 |
importhb.hbwb.var.PublicVariable; |
005 |
|
006 |
importandroid.app.Activity; |
007 |
importandroid.app.Dialog; |
008 |
importandroid.graphics.Bitmap; |
009 |
importandroid.os.AsyncTask; |
010 |
importandroid.view.View; |
011 |
importandroid.view.View.OnClickListener; |
012 |
importandroid.widget.ImageView; |
013 |
|
014 |
/** |
015 |
* @name 异步加载图片 |
016 |
* @author zhang.yue |
017 |
* @create_date 2011-12-15 |
018 |
* @last_edit_author |
019 |
* @last_edit_date |
020 |
* @remark |
021 |
* @edit_remark |
022 |
*/ |
023 |
public classLoadingTopicImageAsyncTask extends |
024 |
AsyncTask<String, Integer,String> { |
025 |
private ImageView imageView = null ; |
026 |
private Topic topic = null ; |
027 |
private Bitmap bt = null ; |
028 |
private Activity activity; |
029 |
private boolean isClick = true ; |
030 |
private String tag = "" ; |
031 |
|
032 |
/** |
033 |
* 重写构造 |
034 |
* |
035 |
* @paramheaderView |
036 |
* 图像控件 |
037 |
* @paramtopic |
038 |
* @paramactivity |
039 |
* @paramisClick |
040 |
* 是否可以点击放大 |
041 |
*/ |
042 |
public LoadingTopicImageAsyncTask(ImageViewimageView, Topic topic, |
043 |
Activity activity, booleanisClick, String tag) { |
044 |
super (); |
045 |
this .imageView = imageView; |
046 |
this .topic = topic; |
047 |
this .activity = activity; |
048 |
this .isClick = isClick; |
049 |
this .tag = tag; |
050 |
} |
051 |
|
052 |
@Override |
053 |
protected String doInBackground(String...params) { |
054 |
// 添加如果是本地缓存的从本地读取... |
055 |
bt =PublicVariable.allTopicImage.get(String.valueOf(topic.getTid())); |
056 |
if (bt == null ) { |
057 |
// 图像获取并保存 |
058 |
if (topic.getImage_value().equals( "" )){ |
059 |
bt = null ; |
060 |
} else { |
061 |
bt =PicTool.ReturnBitMap(topic.getImage_value()); |
062 |
} |
063 |
} |
064 |
if (bt!= null ) { |
065 |
if (bt.getWidth()>PublicVariable.TOPIC_IMAGE_SHOW- 40 ||bt.getHeight()>PublicVariable.TOPIC_IMAGE_SHOW_HEIGHT- 40 ) { |
066 |
bt =PicTool.ChangeSizeBitMap(bt, PublicVariable.TOPIC_IMAGE_SHOW- 40 ); |
067 |
} |
068 |
} |
069 |
return null ; |
070 |
} |
071 |
|
072 |
@Override |
073 |
protected void onPostExecute(String result) { |
074 |
super .onPostExecute(result); |
075 |
if (imageView != null &&topic != null && !tag.equals( "" )) { |
076 |
if (imageView.getTag().toString().equals(tag)) { |
077 |
if (bt != null ) { |
078 |
if (isClick) { |
079 |
//点击大图效果 |
080 |
imageView.setOnClickListener(newOnClickListener() { |
081 |
@Override |
082 |
publicvoid onClick(View v) { |
083 |
ShowImageClickListener(bt,activity); |
084 |
} |
085 |
}); |
086 |
} |
087 |
imageView.setImageBitmap(bt); |
088 |
PublicVariable.allTopicImage.put( |
089 |
String.valueOf(topic.getTid()),bt); // 保存到数组中 |
090 |
} |
091 |
} |
092 |
} |
093 |
} |
094 |
|
095 |
/** |
096 |
* 图片点击功能事件处理 |
097 |
* |
098 |
* @parambt |
099 |
* @paramactivity |
100 |
*/ |
101 |
public static voidShowImageClickListener(Bitmap bt, Activity activity) { |
102 |
Bitmap maxBt =PicTool.ChangeSizeBitMap(bt, |
103 |
PublicVariable.TOPIC_IMAGE_SHOW); |
104 |
ImageView showImageView = newImageView(activity); |
105 |
showImageView.setImageBitmap(maxBt); |
106 |
//两个嵌套的TabHost中,必须使用activity.getParent() |
107 |
//在首页中使用并无报错,全局使用 |
108 |
Dialog d = newDialog(activity.getParent(), hb.hbwb.R.style.no_back_title_dialog); |
109 |
d.setContentView(showImageView); |
110 |
d.setCanceledOnTouchOutside( true ); |
111 |
d.show(); |
112 |
} |
113 |
|
114 |
} |
·PublicVariable.allTopicImage是我们写的一个HashMap,存储Bitmap的一个集合,如果在程序的运行过程中多次显示同一个图片即可直接从这里面取出Bitmap并显示,可以节省网上加载或者sd卡读取的步骤,键值是topic的tid,一个微博仅显示一张图片。
·如果上述的Bitmap集合中不存在图片的话,调用PicTool类中的ReturnBitMap返回SD卡上缓存的图片。
·如果图片超过我们规定的一个大小,那么调用PicTool类中的ChangeSizeBitMap方法。
·onPostExecute方法是线程执行完成后,我们将Bitmap对象给予Image图片控件并显示出来。在这里我们还添加了点击事件。
3. ReturnBitMap解析
001 |
/** |
002 |
* 返回图片文件用 |
003 |
* |
004 |
* @paramurl |
005 |
* 图片url地址 |
006 |
*@return |
007 |
*/ |
008 |
public static Bitmap ReturnBitMap(String url) { |
009 |
String imgName =StringTool.GetImageNameForUrl(url); |
010 |
if (imgName.equals( "" )) { |
011 |
return null ; |
012 |
} else { |
013 |
FileTool ft = newFileTool(); |
014 |
String path =FileFinals.SDCARDROOT + File.separator + FileFinals.SDCARDIMAGEPATH; |
015 |
// 图片文件存在并且不是刷新的时候 |
016 |
if (ft.IsFileExist(imgName +FileFinals.IMAGE_SYSTEM_EXT, FileFinals.SDCARDIMAGEPATH)) { |
017 |
Bitmap bm =ReturnLocalBitMap(path + File.separator + imgName); |
018 |
if (bm== null ){ |
019 |
returnReturnWebBitMap(url, imgName, path); |
020 |
} else { |
021 |
returnbm; |
022 |
} |
023 |
} else { |
024 |
// 先删除原有的 |
025 |
returnReturnWebBitMap(url, imgName, path); |
026 |
} |
027 |
} |
028 |
} |
029 |
|
030 |
|
031 |
/** |
032 |
* 本地获取图片信息 |
033 |
* |
034 |
* @parampath |
035 |
* 图片路径 |
036 |
*@return bitmap对象 |
037 |
*/ |
038 |
public static Bitmap ReturnLocalBitMap(Stringpath) { |
039 |
Bitmap bitmap =BitmapFactory.decodeFile(path + ".image" ); |
040 |
return bitmap; |
041 |
} |
042 |
|
043 |
/** |
044 |
* 抓取远程图片 |
045 |
* |
046 |
* @paramurl |
047 |
* 图片地址 |
048 |
* @paramimgName |
049 |
* 图片名 |
050 |
* @parampath |
051 |
* 地址 |
052 |
*@return |
053 |
*/ |
054 |
public static Bitmap ReturnWebBitMap(Stringurl, String imgName, String path) { |
055 |
|
056 |
Bitmap bitmap = null ; |
057 |
try { |
058 |
// 图片大小判断操作 |
059 |
InputStream size_is =HTMLTool.GetHttpConnection(url).getInputStream(); |
060 |
BitmapFactory.Options op = new BitmapFactory.Options(); |
061 |
op.inJustDecodeBounds = true ; |
062 |
@SuppressWarnings ( "unused" ) |
063 |
Bitmap size_bitmap =BitmapFactory.decodeStream(size_is, null , op); |
064 |
op.inJustDecodeBounds = true ; // 仅获取宽高信息,不加载整个图片 |
065 |
boolean isop = false ; |
066 |
int size =PublicVariable.TOPIC_IMAGE_SHOW; // 设定获取的大小不能超过宽高 |
067 |
int bili = 0 ; // 压缩比例 |
068 |
BitmapFactory.Options op_new= new BitmapFactory.Options(); |
069 |
if (op.outWidth>op.outHeight){ |
070 |
if (op.outWidth> size) { |
071 |
bili =op.outWidth / size; |
072 |
isop = true ; |
073 |
} |
074 |
} else { |
075 |
if (op.outHeight> size) { |
076 |
bili =op.outHeight / size; |
077 |
|
078 |
isop = true ; |
079 |
} |
080 |
} |
081 |
|
082 |
// 根据比例判断 |
083 |
if (bili != 0 ) { |
084 |
if (bili < 2 ) { |
085 |
bili = 2 ; |
086 |
} else { |
087 |
bili = bili* 2 - 2 ; |
088 |
} |
089 |
if (bili% 2 != 0 ){ |
090 |
bili =bili+ 1 ; |
091 |
} |
092 |
op_new.inSampleSize= bili; |
093 |
} else { |
094 |
isop = false ; |
095 |
} |
096 |
System.out.println(bili); |
097 |
size_bitmap = null ; |
098 |
size_is.close(); |
099 |
|
100 |
// 如果进行过压缩,加载处理后的图片,如果没有直接加载 |
101 |
InputStream is =HTMLTool.GetHttpConnection(url).getInputStream(); |
102 |
if (isop) { |
103 |
op_new.inPreferredConfig= Bitmap.Config.ARGB_4444; |
104 |
op_new.inPurgeable= true ; |
105 |
op_new.inInputShareable= true ; |
106 |
bitmap =BitmapFactory.decodeStream(is, null , op_new); |
107 |
} else { |
108 |
bitmap =BitmapFactory.decodeStream(is); |
109 |
} |
110 |
|
111 |
|
112 |
// 将图片写入到内存卡中,做为缓存,将来直接本地读取 |
113 |
WriteBitmapToSdCard(FileFinals.SDCARDIMAGEPATH,path + File.separator + imgName, bitmap); |
114 |
|
115 |
is.close(); |
116 |
|
117 |
} catch (Exception e) { |
118 |
return null ; |
119 |
} |
120 |
return bitmap; |
121 |
} |
·StringTool.GetImageNameForUrl是截取文件名,比如:http://www.huoban168.com/huoban/upload/topic/2012/02/weibo_1329196372_1112447.png,返回的是weibo_1329196372_1112447.png这一部分的内容。
·接下来查看当前SD卡的缓存文件路径下有没有这张图,如果有的话,直接调用ReturnLocalBitMap加载图片,如果没有的话,调用ReturnWebBitMap方法去调用网络图片并下载下来。
·后缀名都加了一个".image",目的是防止Android的图片浏览器去加载这些图片。
·ReturnWebBitMap中,先通过HTMLConnection获取图片的InputStream二进制流,然后通过 op.inJustDecodeBounds = true; // 这个方法,获取一个只有高度等属性的Bitmap对象,接着根据图片原始大小获取需要大小的压缩比例op_new.inSampleSize = bili;。
·最后取到压缩过的Bitmap对象写入到内存卡或者直接显示均可。至此,图片的下载和保存已完成。
4. ChangeSizeBitMap解析
01 |
/** |
02 |
* 等比例缩放图片 |
03 |
* |
04 |
* @param bt |
05 |
* bitmap图片对象 |
06 |
* @param resize |
07 |
* 状态,是否进行缩放 |
08 |
* @return bitmap图片对象 |
09 |
*/ |
10 |
publicstatic Bitmap ChangeSizeBitMap(Bitmap bt, int resize) { |
11 |
intsrc_width = bt.getWidth(); |
12 |
intsrc_height = bt.getHeight(); |
13 |
floatwidth = ( float )bt.getWidth(); |
14 |
floatheight = ( float )bt.getHeight(); |
15 |
floatbmpWidth = ( float ) resize / width; |
16 |
floatbmpHeight = ( float ) resize / height; |
17 |
if (width >= height) { |
18 |
if (width > resize) { |
19 |
bmpWidth= ( float ) resize / height; |
20 |
bmpHeight= bmpWidth; |
21 |
} |
22 |
} else { |
23 |
if (height > resize) { |
24 |
bmpHeight= ( float ) resize / height; |
25 |
bmpWidth= bmpHeight; |
26 |
} |
27 |
} |
28 |
|
29 |
if (bmpWidth > 1 || bmpHeight > 1 ) { |
30 |
returnbt; |
31 |
} |
32 |
Matrixmatrix = new Matrix(); |
33 |
matrix.postScale(bmpWidth,bmpHeight); |
34 |
BitmapresizeBmp = Bitmap.createBitmap(bt, 0 , 0 , src_width, src_height, matrix, true ); |
35 |
returnresizeBmp; |
36 |
} |