分享一個(gè)輕量級(jí)圖片加載類 ImageLoader
ImageLoader 這類的 圖片加載網(wǎng)絡(luò)上一大推,像比較出名的有nostra13 的-Image-Loader圖片加載,xUtil的圖片加載,還有 Facebook 的 Fresco 。很多,但本著求學(xué)的態(tài)度,最近在做項(xiàng)目時(shí)有圖片加載這個(gè)需求就自己寫了個(gè)輕量級(jí)的 (本地)圖片緩存加載 功能,分享給各位。
里面涉及了 LruCache ,ExecutorService,處理大圖的 BitmapFactory 原理,view.setTag() .
好了,不多說(shuō),先一步一步來(lái):
首先看一下我封裝的類怎么使用:
// 本地照片絕對(duì)路徑
String imageUrl = (String) t;
// 得到 ImageView
ImageView grid_item = holder.getView(R.id.grid_item);
// 設(shè)置 tag ,標(biāo)記用的,反正圖片顯示錯(cuò)位
grid_item.setTag(imageUrl);
/**
* 顯示 圖片
*
* @param context
* : 上下文
* @param imageView
* : ImageView 控件
* @param sourcePath
* : 圖片 地址
* @param r_Id
* : 默認(rèn) 圖片 id ,R.drowable.id;
* @param callback
* :圖片顯示 回調(diào)
*/
new ImageLoader().displayBmp(mContext,grid_item, imageUrl, R.drawable.img_bg,this);
是不是很簡(jiǎn)單。
接下來(lái)具體分析 ImageLoader 這個(gè)類:
都知道手機(jī)的內(nèi)存有限,不可能將所有的圖片都加進(jìn)內(nèi)存,所以android 提供了一個(gè) LruCache 方法,用到的 算法 是 :近期最少使用算法 ,及在圖片不斷的加進(jìn)緩存,最少使用的圖片也在不斷的移除緩存 ,從而避免的內(nèi)存不夠的問題。
LruCache 的初始化代碼如下:
public ImageLoader() {
// 取應(yīng)用內(nèi)存的 8/1 作為 圖片緩存用
int cacheSize = maxMemory / 8;
// 得到 LruCache
mLruCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
}
};
}
/**
* 將圖片存儲(chǔ)到LruCache
*/
public void putBitmapToLruCache(String key, Bitmap bitmap) {
if (getBitmapFromLruCache(key) == null && mLruCache != null) {
mLruCache.put(key, bitmap);
}
}
/**
* 從LruCache緩存獲取圖片
*/
public Bitmap getBitmapFromLruCache(String key) {
return mLruCache.get(key);
}
LruCache 就像 HashMap 一樣 利用put 和 get 得到緩存的東西。
接著看 圖片的 具體加載,先把代碼貼出來(lái):
/**
* 顯示 圖片
*
* @param context
* : 上下文
* @param imageView
* : ImageView 控件
* @param sourcePath
* : 圖片 地址
* @param r_Id
* : 默認(rèn) 圖片 id ,R.drowable.id;
* @param callback
* :圖片顯示 回調(diào)
*/
public void displayBmp(final Context context, final ImageView imageView, final String sourcePath, final int r_Id,
final ImageCallback callback) {
final String path;
if (!TextUtils.isEmpty(sourcePath)) {
path = sourcePath;
} else {
return;
}
// 先 試著 從 緩存 得到 圖片 , path 作為 圖片的 key
Bitmap bmp = mLruCache.get(path);
if (bmp != null) {
if (callback != null) {
// 回調(diào) 圖片 顯示
callback.imageLoad(imageView, bmp, sourcePath);
}
// imageView.setImageBitmap(bmp);
return;
}
// 如果 bmp == null ,給 imageView 顯示默認(rèn)圖片
imageView.setImageResource(r_Id);
// 啟動(dòng) 線程池
threadPoolUtils.getExecutorService().execute(new Runnable() {
Bitmap bitmap = null;
@Override
public void run() {
// TODO Auto-generated method stub
try {
// 加載 圖片 地址 對(duì)應(yīng) 的 縮略圖
bitmap = revitionImageSize(imageView, sourcePath);
} catch (Exception e) {
}
if (bitmap == null) {
try {
// 如果 縮略圖 沒加載成功 顯示 默認(rèn) 設(shè)置的圖片
bitmap = BitmapFactory.decodeResource(context.getResources(), r_Id);
} catch (Exception e) {
}
}
if (path != null && bitmap != null) {
// 將 縮略圖 放進(jìn) 緩存 , path 作為 key
putBitmapToLruCache(path, bitmap);
}
if (callback != null) {
handler.post(new Runnable() {
@Override
public void run() {
// 回調(diào) 圖片 顯示
callback.imageLoad(imageView, bitmap, sourcePath);
}
});
}
}
});
}
代碼不是狠多,主要就是 先從緩存加載圖片,當(dāng)加載圖片為空時(shí),再?gòu)氖謾C(jī)的圖片地址加載圖片
bitmap = revitionImageSize(imageView, sourcePath);
加載緩存圖片就不多說(shuō)了,看的也明白, mLruCache.get(key);就這么簡(jiǎn)單
具體分析 revitionImageSize() 這個(gè)方法吧:
public Bitmap revitionImageSize(ImageView imageView, String path) throws IOException {
// 得到 布局 ImageView 的 寬高
int img_width = imageView.getWidth();
int img_height = imageView.getHeight();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(new File(path)));
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, options);
in.close();
int height = options.outHeight;
int width = options.outWidth;
Bitmap bitmap = null;
int inSampleSize = 1;
// 計(jì)算出實(shí)際寬高和目標(biāo)寬高的比率
final int heightRatio = Math.round((float) height / (float) img_height);
final int widthRatio = Math.round((float) width / (float) img_width);
// 選擇寬和高中最小的比率作為inSampleSize的值,這樣可以保證最終圖片的寬和高
// 一定都會(huì)大于等于目標(biāo)的寬和高。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
// 調(diào)用上面定義的方法計(jì)算inSampleSize值
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
in = new BufferedInputStream(new FileInputStream(new File(path)));
bitmap = BitmapFactory.decodeStream(in, null, options);
in.close();
return bitmap;
}
代碼我也寫了注釋了 ,一般在加載圖片時(shí)都有對(duì)圖片一定的壓縮處理避免OOM,所以上面的處理方法也是挺常見的,對(duì)要顯示 圖片 根據(jù) imageview 控件大小進(jìn)行一定的壓縮。
如果對(duì) 圖片壓縮處理不是很理解的朋友這么我簡(jiǎn)單解釋一下:
首先加載完圖片:
BufferedInputStream in = new BufferedInputStream(new FileInputStream(new File(path)));
然后:
options.inJustDecodeBounds = true;
再接著:
int height = options.outHeight; int width = options.outWidth;
這時(shí)候注意 程序并沒有把圖片真正的加載進(jìn)來(lái),options.inJustDecodeBounds = true;
這句在起作用,但圖片的寬和高 的信息我們卻得到了,就可以處理壓縮圖片了!
壓縮完圖片再:
options.inJustDecodeBounds = false;
重新得到壓縮后的圖片:
bitmap = BitmapFactory.decodeStream(in, null, options);
解釋完畢。
仔細(xì)看代碼的同學(xué)會(huì)發(fā)現(xiàn) displayBmp() 方法里面有個(gè) 回調(diào)參數(shù):
回調(diào)接口如下:
/**
* 顯示圖片回調(diào)
*
* @author Administrator
*
*/
public interface ImageCallback {
public void imageLoad(ImageView imageView, Bitmap bitmap, Object... params);
}
具體實(shí)現(xiàn)是在顯示圖片的地方回調(diào)的:
/**
* 圖片 緩存回調(diào)
*/
@Override
public void imageLoad(ImageView imageView, Bitmap bitmap, Object... params) {
if (imageView != null && bitmap != null) {
String url = (String) params[0];
// 判斷 這里的 url 是否 對(duì)應(yīng) imageView.getTag()
// 如果 將這句 判斷 去掉 那么 就會(huì)出現(xiàn) 經(jīng)常出現(xiàn)的 圖片 顯示 錯(cuò)位 問題 !?。。?
if (url != null && url.equals((String) imageView.getTag())) {
((ImageView) imageView).setImageBitmap(bitmap);
}
}
}
代碼注釋的地方也寫了,不太理解的同學(xué)可以私信交流,另外附上我 github github連接上的源碼,可以下載下了運(yùn)行方便好理解:
為了你方便使用,在你的項(xiàng)目中添加如下依賴即可:
dependencies {
compile 'com.zts:imageloader:1.1.1'
}
- 一起動(dòng)手編寫Android圖片加載框架
- Android開發(fā)中ImageLoder進(jìn)行圖片加載和緩存
- Android圖片加載緩存框架Glide
- Android圖片加載的緩存類
- Android程序開發(fā)ListView+Json+異步網(wǎng)絡(luò)圖片加載+滾動(dòng)翻頁(yè)的例子(圖片能緩存,圖片不錯(cuò)亂)
- Android開發(fā)之ImageLoader本地緩存
- Android Universal ImageLoader 緩存圖片
- 從源代碼分析Android Universal ImageLoader的緩存處理機(jī)制
- Android開發(fā)之ImageLoader使用詳解
相關(guān)文章
Android Studio使用recyclerview實(shí)現(xiàn)展開和折疊功能(在之前的微信頁(yè)面基礎(chǔ)之上)
這篇文章主要介紹了Android Studio使用recyclerview實(shí)現(xiàn)展開和折疊(在之前的微信頁(yè)面基礎(chǔ)之上),本文通過截圖實(shí)例代碼給大家講解的非常詳細(xì),需要的朋友可以參考下2020-03-03
Android 自定義精美界面包含選項(xiàng)菜單 上下文菜單及監(jiān)聽詳解流程
這篇文章主要介紹了一個(gè)Android實(shí)例小項(xiàng)目,它包含了選項(xiàng)菜單、上下文菜單及其對(duì)應(yīng)的監(jiān)聽事件,它很小,但這部分功能在Android開發(fā)中很常見,需要的朋友來(lái)看看吧2021-11-11
Android ListView萬(wàn)能適配器實(shí)例代碼
本文主要介紹Android ListView萬(wàn)能適配器,這里整理了詳細(xì)的資料及實(shí)現(xiàn)代碼,以及實(shí)現(xiàn)效果圖,有需要的小伙伴可以參考下2016-09-09
Material Design系列之自定義Behavior支持所有View
這篇文章主要為大家詳細(xì)介紹了Material Design系列之自定義Behavior支持所有View,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
Golang+Android基于HttpURLConnection實(shí)現(xiàn)的文件上傳功能示例
這篇文章主要介紹了Golang+Android基于HttpURLConnection實(shí)現(xiàn)的文件上傳功能,結(jié)合具體實(shí)例形式分析了Android基于HttpURLConnection的客戶端結(jié)合Go語(yǔ)言服務(wù)器端實(shí)現(xiàn)文件上傳功能的操作技巧,需要的朋友可以參考下2017-03-03
建造者模式_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
建造者實(shí)現(xiàn)抽象類的所有未實(shí)現(xiàn)的方法,具體來(lái)說(shuō)一般是兩項(xiàng)任務(wù),組建產(chǎn)品;返回組建好的產(chǎn)品2017-08-08
如何獲取Android設(shè)備掛載的所有存儲(chǔ)器
這篇文章主要為大家詳細(xì)介紹了如何獲取Android設(shè)備掛載的所有存儲(chǔ)器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
Android?APP瘦身shrinkResources使用問題詳解
這篇文章主要為大家介紹了Android?APP瘦身shrinkResources使用問題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11

