在上一篇專題Android開發之圖片處理專題(一):利用軟引用構建圖片高速緩存中我們講述瞭如何利用軟引用技術構建高速緩存。那麼想要用到圖片,首先得有圖片的來源。一般而言,一個應用的圖片資源都是從服務器處獲得的。今天,我們利用Android開發之網絡請求通信專題(二):基於HttpClient的文件上傳下載裏面封裝好的httpUtils來實現圖片的下載,然後加載到本地配合軟引用緩存使用,以一個listView爲例子來說明。
一、準備工作
二、工具類的編寫
1、回調接口的準備
public interface FreedomCallback {
/**
* @Title: imageLoaded
* @Description: TODO
* @param imageDrawable 傳回的bitmap對象
* @param tag 用於listView查找控件
* @throws
*/
public void imageLoaded(Bitmap imageDrawable, Object tag);
}
2、圖片下載類和異步處理任務
public class ImageDownload {
private static final String TAG = "ImageDownload";
private static String path;
//構建緩存地址
static {
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
File externalStorageDirectory = Environment
.getExternalStorageDirectory();
path = externalStorageDirectory.getAbsolutePath()
+ ConstantValue.IMAGE_PATH;
File directory = new File(path);
if (!directory.exists()) {
directory.mkdirs();
}
} else {
path = null;
Log.e(TAG, "no sdcard.");
}
}
/**
* @Title: loadDrawable
* @Description: 下載圖片
* @param imageUrl
* @param simpleName
* @param imageCallback
* @throws
*/
public void loadDrawable(String imageUrl, String simpleName,
FreedomCallback imageCallback) {
new FreedomLoadTask(imageCallback).execute(imageUrl, simpleName);
}
/**
* @Title: loadImageFromUrl
* @Description: 根據地址下載圖片
* @param url
* @return
* @throws
*/
public Bitmap loadImageFromUrl(String url) {
InputStream i = null;
try {
FreedomHttpClientUtil util = new FreedomHttpClientUtil();
util.open(url, FreedomHttpClientUtil.GET);
util.post();
i = util.openInputStream();
//拿到流後創建bitmap
Bitmap bitmap = BitmapFactory.decodeStream(i);
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @Title: saveImage2SDCard
* @Description: 將圖片存放到SD卡中
* @param simpleName
* @param bitmap
* @throws
*/
public void saveImage2SDCard(String simpleName, Bitmap bitmap) {
if (StringUtils.isNotBlank(path)) {
File file = new File(new File(path), simpleName + ".png");
FileOutputStream fOut = null;
try {
fOut = new FileOutputStream(file);
//將bitmap寫入流中
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
} catch (Exception e) {
org.cao.optimization.util.Log.info("文件無法創建");
e.printStackTrace();
} finally {
if (fOut != null)
try {
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* @ClassName: FreedomLoadTask
* @author victor_freedom ([email protected])
* @createddate 2015-1-31 下午11:39:18
* @Description: 圖片下載,返回一個bitmap對象
*/
class FreedomLoadTask extends AsyncTask<String, Integer, Bitmap> {
private FreedomCallback imageCallback;
public FreedomLoadTask(FreedomCallback imageCallback) {
this.imageCallback = imageCallback;
}
@Override
protected Bitmap doInBackground(String... params) {
// 傳入兩個 參數,一個是下載路徑,一個是文件名稱
Bitmap bitmap = loadImageFromUrl(params[0]);
if (bitmap != null) {
// 保存到SD卡中
saveImage2SDCard(params[1], bitmap);
// 加入緩存中
ImageCache.getInstance().cacheImage(
new Image(params[1], bitmap));
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
// 下載成功後執行回調接口
imageCallback.imageLoaded(result, null);
super.onPostExecute(result);
}
}
}
三、界面處理
1、Adapter處getView處理
//拿到圖片的名字
final String id = infos.getName();
//給此處ImageView空間設置id
continer.image.setTag(id);
//根據名字首先從軟應用中拿到bitmap
Image image = ImageCache.getInstance().getImage(id);
Bitmap bitmap = image.getBitmap();
//判斷bitmap是否爲空
if (bitmap == null) {
// 判斷本地SDCard中是否有相關文件
if (Image.hasFile(id)) {
InputStream is;
try {
is = new FileInputStream(Image.getFileFromSDCard(id));
bitmap = BitmapFactory.decodeStream(is);
ImageCache.getInstance().cacheImage(new Image(id, bitmap));
continer.image.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else {
//本地也沒有,就從服務器下載
ImageDownload download = new ImageDownload();
download.loadDrawable(news.getImgUrl(), id + "",
new FreedomCallback() {
@Override
public void imageLoaded(Bitmap imageDrawable,
Object tag) {
if (imageDrawable != null) {
//構建adapter時傳入的回調接口,如果下載圖片成功,回調此方法
listCallback.imageLoaded(imageDrawable, id);
}
}
});
continer.image.setImageResource(R.drawable.ic_launcher);
}
} else {
//如果軟引用還存在,則直接拿來用
continer.image.setImageBitmap(bitmap);
}
2、ListView初始化相關處理
adapter = new NewsAdapter(context, new ListCallback() {
@Override
public void imageLoaded(Bitmap bitmap, Object tag) {
//根據回調傳回的tag找到對應的ImageView
ImageView imageView = (ImageView) listView.findViewWithTag(tag);
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
});
listView = new ListView(context);
listView.setAdapter(adapter);
四、圖片壓縮
//圖片質量壓縮
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 質量壓縮方法,這裏100表示不壓縮,把壓縮後的數據存放到baos中
int options = 100;
while (baos.toByteArray().length / 1024 > 100) { // 循環判斷如果壓縮後圖片是否大於100kb,大於繼續壓縮
baos.reset();// 重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 這裏壓縮options%,把壓縮後的數據存放到baos中
options -= 10;// 每次都減少10
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把壓縮後的數據baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream數據生成圖片
return bitmap;
}
//圖片寬高壓縮
public static Bitmap getimage(String srcPath) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
// 開始讀入圖片,此時把options.inJustDecodeBounds 設回true了
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此時返回bm爲空
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
// 這個設置圖片的寬高,根據實際需要設定
float hh = 800f;// 這裏設置高度爲800f
float ww = 480f;// 這裏設置寬度爲480f
// 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
int be = 1;// be=1表示不縮放
if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {// 如果高度高的話根據寬度固定大小縮放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;// 設置縮放比例
// 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
return compressImage(bitmap);// 壓縮好比例大小後再進行質量壓縮
}
有時候,我們還可以讓系統自動的給我們壓縮,只要確保不會出現OOM錯誤又能讓圖片以最大性能去展示
File file = Image.getFileFromSDCard(name, "pure");
Options opts = new Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), opts);
opts.inJustDecodeBounds = false;
// 根據設定的值讓系統自動壓縮,這行代碼的參數是第一個是BitmapFactory.Options,第二個參數是調整後圖片最小的寬或者高,第三個參數是調整後圖片的內存佔用量上限。
opts.inSampleSize = Util.computeSampleSize(opts, 600,(int) (1 * 1024 * 1024));
opts.inDither = false;
<span style="white-space:pre"> </span>opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), opts);
圖片壓縮的步驟一般設定在圖片下載完之後。
至此,圖片從下載到顯示的所有流程,我們已經實現。當然,這只是一種實現方式,圖片的異步加載還可以有其他的實現方式。今天我們回顧了Android開發之網絡請求通信專題(二):基於HttpClient的文件上傳下載的內容裏封裝的httputils,下一篇我們利用之前所學的線程池ThreadPoolExcutor配合 Android開發之網絡請求通信專題(一):基於HttpURLConnection的請求通信裏所講的httpurlconection來實現listview的大量圖片的異步加載。