編輯:關於Android編程
ImageLoader 這類的 圖片加載網絡上一大推,像比較出名的有nostra13 的-Image-Loader圖片加載,xUtil的圖片加載,還有 Facebook 的 Fresco 。很多,但本著求學的態度,最近在做項目時有圖片加載這個需求就自己寫了個輕量級的 (本地)圖片緩存加載 功能,分享給各位。
裡面涉及了 LruCache ,ExecutorService,處理大圖的 BitmapFactory 原理,view.setTag() .
好了,不多說,先一步一步來:
首先看一下我封裝的類怎麼使用:
// 本地照片絕對路徑 String imageUrl = (String) t; // 得到 ImageView ImageView grid_item = holder.getView(R.id.grid_item); // 設置 tag ,標記用的,反正圖片顯示錯位 grid_item.setTag(imageUrl); /** * 顯示 圖片 * * @param context * : 上下文 * @param imageView * : ImageView 控件 * @param sourcePath * : 圖片 地址 * @param r_Id * : 默認 圖片 id ,R.drowable.id; * @param callback * :圖片顯示 回調 */ new ImageLoader().displayBmp(mContext,grid_item, imageUrl, R.drawable.img_bg,this);
是不是很簡單。
接下來具體分析 ImageLoader 這個類:
都知道手機的內存有限,不可能將所有的圖片都加進內存,所以android 提供了一個 LruCache 方法,用到的 算法 是 :近期最少使用算法 ,及在圖片不斷的加進緩存,最少使用的圖片也在不斷的移除緩存 ,從而避免的內存不夠的問題。
LruCache 的初始化代碼如下:
public ImageLoader() { // 取應用內存的 8/1 作為 圖片緩存用 int cacheSize = maxMemory / 8; // 得到 LruCache mLruCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getByteCount(); } }; } /** * 將圖片存儲到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 得到緩存的東西。
接著看 圖片的 具體加載,先把代碼貼出來:
/** * 顯示 圖片 * * @param context * : 上下文 * @param imageView * : ImageView 控件 * @param sourcePath * : 圖片 地址 * @param r_Id * : 默認 圖片 id ,R.drowable.id; * @param callback * :圖片顯示 回調 */ 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) { // 回調 圖片 顯示 callback.imageLoad(imageView, bmp, sourcePath); } // imageView.setImageBitmap(bmp); return; } // 如果 bmp == null ,給 imageView 顯示默認圖片 imageView.setImageResource(r_Id); // 啟動 線程池 threadPoolUtils.getExecutorService().execute(new Runnable() { Bitmap bitmap = null; @Override public void run() { // TODO Auto-generated method stub try { // 加載 圖片 地址 對應 的 縮略圖 bitmap = revitionImageSize(imageView, sourcePath); } catch (Exception e) { } if (bitmap == null) { try { // 如果 縮略圖 沒加載成功 顯示 默認 設置的圖片 bitmap = BitmapFactory.decodeResource(context.getResources(), r_Id); } catch (Exception e) { } } if (path != null && bitmap != null) { // 將 縮略圖 放進 緩存 , path 作為 key putBitmapToLruCache(path, bitmap); } if (callback != null) { handler.post(new Runnable() { @Override public void run() { // 回調 圖片 顯示 callback.imageLoad(imageView, bitmap, sourcePath); } }); } } }); }
代碼不是狠多,主要就是 先從緩存加載圖片,當加載圖片為空時,再從手機的圖片地址加載圖片
bitmap = revitionImageSize(imageView, sourcePath);
加載緩存圖片就不多說了,看的也明白, mLruCache.get(key);就這麼簡單
具體分析 revitionImageSize() 這個方法吧:
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; // 計算出實際寬高和目標寬高的比率 final int heightRatio = Math.round((float) height / (float) img_height); final int widthRatio = Math.round((float) width / (float) img_width); // 選擇寬和高中最小的比率作為inSampleSize的值,這樣可以保證最終圖片的寬和高 // 一定都會大於等於目標的寬和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; // 調用上面定義的方法計算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; }
代碼我也寫了注釋了 ,一般在加載圖片時都有對圖片一定的壓縮處理避免OOM,所以上面的處理方法也是挺常見的,對要顯示 圖片 根據 imageview 控件大小進行一定的壓縮。
如果對 圖片壓縮處理不是很理解的朋友這麼我簡單解釋一下:
首先加載完圖片:
BufferedInputStream in = new BufferedInputStream(new FileInputStream(new File(path)));
然後:
options.inJustDecodeBounds = true;
再接著:
int height = options.outHeight; int width = options.outWidth;
這時候注意 程序並沒有把圖片真正的加載進來,options.inJustDecodeBounds = true;
這句在起作用,但圖片的寬和高 的信息我們卻得到了,就可以處理壓縮圖片了!
壓縮完圖片再:
options.inJustDecodeBounds = false;
重新得到壓縮後的圖片:
bitmap = BitmapFactory.decodeStream(in, null, options);
解釋完畢。
仔細看代碼的同學會發現 displayBmp() 方法裡面有個 回調參數:
回調接口如下:
/** * 顯示圖片回調 * * @author Administrator * */ public interface ImageCallback { public void imageLoad(ImageView imageView, Bitmap bitmap, Object... params); }
具體實現是在顯示圖片的地方回調的:
/** * 圖片 緩存回調 */ @Override public void imageLoad(ImageView imageView, Bitmap bitmap, Object... params) { if (imageView != null && bitmap != null) { String url = (String) params[0]; // 判斷 這裡的 url 是否 對應 imageView.getTag() // 如果 將這句 判斷 去掉 那麼 就會出現 經常出現的 圖片 顯示 錯位 問題 !!!! if (url != null && url.equals((String) imageView.getTag())) { ((ImageView) imageView).setImageBitmap(bitmap); } } }
代碼注釋的地方也寫了,不太理解的同學可以私信交流,另外附上我 github github連接上的源碼,可以下載下了運行方便好理解:
為了你方便使用,在你的項目中添加如下依賴即可:
dependencies { compile 'com.zts:imageloader:1.1.1' }
本節學習系統中特殊的廣播接收者。我們前面幾節不是說了,當廣播接受者一旦注冊到系統中,當系統發送的廣播和你注冊的廣播的action匹配時,系統就會啟動廣播接收者所在的進程。
導入Android工程出現unable to get system library for the project 錯誤提示問題與現象:當向eclipse導
FloatingActionButton項目在github上的主頁:https://github.com/futuresimple/android-floating-ac
前幾天看見開源項目效果好贊,看了下代碼,實現大致就是在原界面之上覆蓋一成自定義的View,獲取到點擊的那個View的內容(Bitmap),然後在覆蓋的那個自定義View的