Android 大尺寸圖片加載

Android 中圖片的加載是個很頭疼的問題,不過還好我們有Glide(逃跑臉..)
不過當(dāng)你有要顯示一張遠(yuǎn)超手機(jī)尺寸的超大圖片的需求時,Glide也幫不了你,我現(xiàn)在慌得一B(躺草地)
為什么慌呢,因?yàn)殚L這樣


全部加載

可以看到,首先顯示上有問題,其次整張圖片加載到了內(nèi)存中,我們用 Android Profiler 抓一下看看占了多少內(nèi)存


image.png

我滴乖乖,占了快 34M,還好我手機(jī)分配給應(yīng)用的內(nèi)存夠大。那么要如何解決這兩個問題呢?且看分解:

一.圖片加載占用內(nèi)存的計(jì)算

影響圖片在內(nèi)存中大小的有三個元素:
1.圖片原始寬高
2.圖片的色彩空間
3.圖片的縮放比

(1)圖片原始寬高:它們的乘積代表圖片的總像素點(diǎn)數(shù)
(2)圖片的色彩空間:每個像素點(diǎn)的信息,占用多少字節(jié),比如 Bitmap.Config.ARGB8888 代表每個色彩通道占8bit位 總共就是 4個字節(jié),可以在 BitmapFactory.Options 的 inPreferredConfig 屬性進(jìn)行調(diào)節(jié),常用的還有 Bitmap.Config.RGB565
(3)圖片的縮放比:對圖片原始寬高的縮放,影響是次方級的,因?yàn)榉謩e作用在了寬和高上。
它對應(yīng)的設(shè)置在 BitmapFactory.Options 的 inSampleSize 屬性,代表采樣率,默認(rèn)為1,必須大于1且為2的倍數(shù)
比如設(shè)置為 4 ,則圖片的寬高都將變?yōu)樵嫉?1/4 ,那么總像素點(diǎn)數(shù)就變?yōu)榱嗽嫉?1/16

綜上總結(jié)的計(jì)算公式為:

圖片占用內(nèi)存= (原始寬 * 縮放比) * (原始高 * 縮放比) * 色彩空間

我們算一下剛才的圖進(jìn)行驗(yàn)證一下,妹子圖寬 690px 高12287px 直接展示時 縮放比 inSampleSize =1 色彩空間ARGB8888
所以 內(nèi)存= 690 * 1 * 12287 * 1 * 4 = 33912120 和我們圖中抓的基本一致

平時在使用Glide進(jìn)行加載圖片時,框架里幫我們處理了縮放,Glide默認(rèn)會加載并緩存具體尺寸的圖片,同時3.x版本的Glide默認(rèn)使用RGB565的顏色通道,這些都會幫助我們節(jié)省內(nèi)存

二.圖片分區(qū)域加載

既然我們一個屏幕展示不下這張圖,那么我們就顯示一部分,Android 中已經(jīng)提供了 解碼圖片部分區(qū)域的類 BitmapRegionDecoder,使用起來也很簡單


BitmapRegionDecoder

它提供了一系列靜態(tài)方法構(gòu)造實(shí)例


image.png

拿到實(shí)例后 通過 #decodeRegion() 方法,傳入一個 Rect 和 一個BitmapFactory.Options 參數(shù) 即可解碼出一張我們要的圖片解碼區(qū)域就是我們 Rect 指定的范圍,拿到 Bitmap 后當(dāng)然可以為所欲為了

三.手勢檢測

我們已經(jīng)能夠展示大圖的部分區(qū)域了,那么勢必需要提供手勢操作讓用戶滑動或點(diǎn)擊來對圖片加載的區(qū)域或大小進(jìn)行更新也就是需要自定義控件重寫 onTouchEvent方法進(jìn)行處理

這里可以參考洋神的做法:https://blog.csdn.net/lmj623565791/article/details/49300989/

將手勢的處理交給 MoveGestureDetector 然后每次滑動完在 onDraw 里更新 解碼的區(qū)域 Rect

但是有兩個問題,當(dāng)你將洋神的代碼跑起來后,在 7.0 以上的手機(jī)上會發(fā)現(xiàn)圖劃不動,并且得到一個日志:

D/skia: --- SkAndroidCodec::NewFromStream returned null

經(jīng)Google 解決了問題:https://stackoverflow.com/questions/39316069/bitmapfactory-decodestream-from-assets-returns-null-on-android-7

大致就是 Google改了 BitmapFactory.cpp的代碼,我們需要在兩次decode 之間將 流重置一下 如下:洋神的 LargeImageView.java


LargeImageView.java

第二個問題就是跑起來你會發(fā)現(xiàn)滑動起來很卡,體驗(yàn)很差,因?yàn)閯右粍泳鸵恢痹谥乩L。

解決方法就是不要繼承View ,我看世界地圖那個項(xiàng)目是繼承自 SurfaceView的,將繪制放到單獨(dú)的線程

https://github.com/johnnylambada/WorldMap/blob/master/library/src/com/sigseg/android/map/ImageSurfaceView.java

四.開源項(xiàng)目解決方案

1.這個作者加了手勢縮放,雙擊等的處理,同時對顯示區(qū)域進(jìn)行了緩存,實(shí)測很棒

https://github.com/kareluo/IntensifyImageView

2.一個N年前star的庫,但一直還沒用過

https://github.com/davemorrissey/subsampling-scale-image-view

五.另辟蹊徑的方式

使用WebView 進(jìn)行展示,思路來自N年前聽說有個開源第三方微博客戶端是這么搞的

具體參考:https://blog.csdn.net/android_zhengyongbo/article/details/70225377

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容