Android中的圖片加載一直是很重要的一塊,也是很使人頭疼的一塊,動不動就出現OOM。因此咱們有fresco等優秀的第三方框架,什麼三級緩存,一行代碼就幫咱們輕鬆實現。但當面對超級長超級大分辨率尺寸的圖時,就顯得無能爲力了,若是直接加載到內存中就又會出現OOM。git
實現長圖大圖的加載,最關鍵的類就是BitmapRegionDecoder
他能夠實現對圖片的局部加載。github
mDecoder=BitmapRegionDecoder.newInstance(image,false);
//這裏不能用option來獲取圖片的寬和高,由於通過BitmapRegionDecoder類處理事後的inputstream不能在獲取的其信息,自動返回-1
imageHeight=mDecoder.getHeight();
imageWidth=mDecoder.getWidth();
bmp = mDecoder.decodeRegion(mRect, option);
複製代碼
這裏注意通過BitmapRegionDecoder類處理事後的inputstream不能再用option獲取其信息,會自動返回-1。當建立decoder對象後其實並無將圖片加載到內存中,只有調用了bmp = mDecoder.decodeRegion(mRect, option);
以後纔將這個mrect矩形的圖片局部加載到內存中。canvas
那麼既然Android有這麼方便的類,那咱們豈不是很簡單就能夠本身實現啦!因此參考 鴻洋_的Android 高清加載巨圖方案 拒絕壓縮圖片 這篇博客,咱們能夠本身實現一個簡易的加載長圖框架:數組
ondraw()
和onTouchEvent()
computeScroll()
去輔助滑動option.inBitmap
開啓圖片的複用,進一步減小內存佔用int insamplesize=1;
while (imageWidth>1.6*width) {
imageWidth /= 2;
insamplesize*=2;
}
option.inMutable=true;
option.inSampleSize=insamplesize;
複製代碼
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDecoder!=null) {
option.inBitmap=bmp;
matrix.setScale(scale,scale);
bmp = mDecoder.decodeRegion(mRect, option);
canvas.drawBitmap(bmp,matrix,bitmapPaint);
Log.i("TAG", "onDraw: "+bmp.getByteCount());
}
}
複製代碼
注意:這裏記錄一個小小的坑:當用matrix進行圖片的放大時,必定必定要設置畫筆Paint,設置抗鋸齒等優化,設與不設的差距真的挺大的緩存
private Paint bitmapPaint;
bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
bitmapPaint.setDither(true);
複製代碼
這樣一個簡易的圖片加載框架就實現了,完美的避免了OOM,由於不用把圖片完整的加載到內存中!可是,通過實測,這種方法在加載分辨率比較小的大圖時滑動仍是挺絲滑的,但一遇到分辨率再大一點的,就會感覺到明顯的滑動的卡頓,因而我把目光轉向了subsampling scale image view這個目前應該是最流行的開源框架bash
這裏以原理實現爲主,不想貼不少代碼,具體可本身下載閱讀github地址數據結構
subsampling scale image view經過這個類ImageSource去獲取圖片,因此咱們的圖片資源都須要經過ImageSource去加載,支持從assets,文件,流中加載,從源碼上看他其實就是一個工具類,用於方便加載各個路徑的文件框架
.fullImageSampleSamplSize由private int calculateInSampleSize(float scale)
計算出,這個值應該是咱們首先應該理解的。
他決定了圖片是否須要用BitmapRegionDecoder進行區域加載。若是他的計算結果等於1
,則表示這張圖的分辨率還不夠大,不須要進行切割進行區域加載,因此這種狀況下是最簡單的,直接將圖加載進入,放大縮小,移動,都是經過Matrix來實現的,因此接下來就來講一下Matrix工具
matrix,矩陣,不少關於圖片的功能都能經過他來作一些十分的變換來實現(惋惜當年線代沒學好。。。)好比圖片個人位移,放縮,旋轉等等。subsampling scale image view也用了matrix來實現圖片的放縮和位移,主要方法是
matrix.setPolyToPoly(srcArray, 0, dstArray, 0, 4
); 有兩個數組srA,rray和dstArray dstarray數組決定了圖片在屏幕的位置,而大圖的移動滑動就是經過他來實現的性能
private static class Tile
這個內部類就是切片類,subsampling scale image view中最重要的一個數據結構。
private static class Tile {
private Rect sRect;
private int sampleSize;
private Bitmap bitmap;
private boolean loading;
private boolean visible;
// Volatile fields instantiated once then updated before use to reduce GC.
private Rect vRect;
private Rect fileSRect;
}
複製代碼
他的屬性也很簡單就是用來存儲圖片的一段切片信息,各類rect和bitmap和一個samplesiz其中須要區分一下各個rect
mDecoder.decodeRegion(mRect, option)
區域加載時傳入的rectmatrix.setPolyToP()
來實現的protected void onDraw(Canvas canvas) {
......
if (matrix == null) { matrix = new Matrix(); }
matrix.reset();
setMatrixArray(srcArray, 0, 0, tile.bitmap.getWidth(), 0, tile.bitmap.getWidth(), tile.bitmap.getHeight(), 0, tile.bitmap.getHeight());
if (getRequiredRotation() == ORIENTATION_0) {
setMatrixArray(dstArray, tile.vRect.left, tile.vRect.top, tile.vRect.right, tile.vRect.top, tile.vRect.right, tile.vRect.bottom, tile.vRect.left,
}...
...
matrix.setPolyToPoly(srcArray, 0, dstArray, 0, 4);
canvas.drawBitmap(tile.bitmap, matrix, bitmapPaint);
......
}
複製代碼
TilesInitTask,TileLoadTask ,BitmapLoadTask
subsampling scale image view內部又建立了三個繼承自AsyncTask
的task用來在後臺加載decode圖從而不阻礙ui主線程,更加流暢。 因此當fullImageSampleSize==1
時,就直接用BitmapLoadTask解碼整個圖片不需切割,當期大於1時,就須要用TileLoadTask區域解碼分割後的圖片
Map<Integer, List<Tile>> tileMap
最後介紹這個框架的核心,就是這個map
咱們知道,圖片放得越大,所須要的像素分辨率就要越高才能匹配,要否則就會很模糊。相反,若是圖片縮得很小,就不須要很高的分辨率,多了就浪費了。而Android中就能夠根據 option.inSampleSize來對圖片進行採樣壓縮,減少分辨率。
因此,根據這個原理,subsampling scale image view將其根據須要計算出不一樣的採樣率,當作key,而後根據不一樣的採樣率進行切割,生成List<Tile>
放大的時候,subsampling scale image view會選取合適的採樣率後獲取到List<Tile>
而後進行解碼,而且,他只會解碼顯示的部分,也就是til.visiable
爲true時纔會解碼。否者將其回收。
綜上就是subsampling scale image view的大體實現原理
對比本身實現的和subsampling scale image view,後者在大圖的切片方面作得更好只將大圖切成若干片,在判斷是否可見,若是可見就加載到內存中,否者回收;滑動時只改變矩陣的值進行簡單的位移變換,進一步提高了流暢度,並且根據不一樣放縮比例選擇合適的採樣率,進一步減小內存佔用。本身實現的每滑動一次就要從新解碼繪製好幾回,因此後者性能更高,值得學習。