Android 大圖加載

簡介

這個項目是一個用于Android預(yù)覽大圖片的圖片顯示庫,可實現(xiàn)原始圖片高清顯示,專門針對大圖片做了很多優(yōu)化粪摘,可以順暢顯示,縮放10多兆的高清圖片苔悦。效果如下:


原圖顯示演示

項目地址:https://github.com/kareluo/AndroidPicturePreview

相關(guān)技術(shù)概述

  • 圖片分塊加載
    圖片的分塊加載在地圖繪制的情況上最為明顯椎咧,當(dāng)想獲取一張尺寸很大的圖片的某一小塊區(qū)域時,就用到了圖片的分塊加載蟋座,在Android中BitmapRegionDecoder類的功能就是加載一張圖片的指定區(qū)域脚牍。BitmapRegionDecoder類的使用非常簡單,API很少并且一目了然券膀,如下:

    // 創(chuàng)建實例
    mDecoder = BitmapRegionDecoder.newInstance(mFile.getAbsolutePath(), false);
    
    // 獲取原圖片寬高
    mDecoder.getWidth();
    mDecoder.getHeight();
    
    // 加載(10, 10) - (80, 80) 區(qū)域內(nèi)原始精度的Bitmap對象
    Rect rect = new Rect(10, 10, 80, 80);
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 1;
    
    Bitmap bitmap = mDecoder.decodeRegion(rect, options);
    
    // 回收釋放Native層內(nèi)存
    mDecoder.recycle();
    
  • LruCache
    LruCache使用時一般需要繼承其,并重寫其一些方法芹彬,如sizeOf(), entryRemoved()等。其還提供了一個放create()用于get()獲取不到時自動創(chuàng)建会喝,更加需要可實現(xiàn)其会前。
    其中sizeOf()用于處理獲取緩存對象的大小,比如緩存Bitmap對象時蔚万,可以使用Bitmap的字節(jié)數(shù)作為Bitmap大小的表示临庇,值得注意的一點是,LruCache不能換成大小會變的對象假夺,sizeOf()對同一個對象始終要返回相同的值,如果非要換成會變動的對象梧田,那么可以讓其返回固定值,如下:

    @Override
    protected int sizeOf(Point key, Bitmap value) {
       return value.getRowBytes() * value.getHeight();
    }
    

    entryRemoved()也是一個比較重要的方法裁眯,用于回收某個對象時調(diào)用讳癌,這樣當(dāng)回收Bitmap對象時可以調(diào)用Bitmap對象的recycle()方法主動釋放Bitmap對象的內(nèi)存。
    create()方法是當(dāng)get()獲取不到對象時調(diào)用逢艘,默認(rèn)實現(xiàn)返回null骤菠,根據(jù)需要可重寫其。

  • 手勢處理
    主要用到兩個手勢處理類商乎,分別是ScaleGestureDetectorGestureDetector,前者用于處理縮放手勢爬泥,后者用于處理其余手勢,如移動袍啡,快速滑動,點擊蔗牡,雙擊嗅剖,長按等。
    ScaleGestureDetector專門處理縮放手勢信粮,其比較重要的方法是onScale(ScaleGestureDetector detector),當(dāng)縮放時會不停地回調(diào)這個方法督惰,需要注意的一點是detector.getScaleFactor()獲取到的縮放比例是相對上一次的旅掂,不如放大時一般這個值會是1.1, 1.2, 1.1, ....

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
       mIntensifyView.addScale(detector.getScaleFactor(),
               detector.getFocusX(), detector.getFocusY());
       return true;
    }
    

加載流程

圖片狀態(tài)流程圖
  • NONE(無狀態(tài))
    初始的狀態(tài)觉阅,這個時候的View還沒有設(shè)置圖片資源秘车,NONE狀態(tài)不會自動轉(zhuǎn)變到其他狀態(tài),只有外界設(shè)置了圖片資源后才會進(jìn)行下面的狀態(tài)的轉(zhuǎn)移痴柔。

  • SRC(鎖定資源)
    當(dāng)外界設(shè)置了圖片資源后疫向,View便具有了圖片源豪嚎,這是就到了SRC狀態(tài)。

  • LOAD(加載寬高)
    到達(dá)LOAD狀態(tài)前侈询,需要加載圖片的真實寬高,真實寬高加載完成后就到了LOAD狀態(tài)囊嘉,LOAD狀態(tài)還沒有真正加載圖片,這是有必要的扭粱,如界面不可見,圖片不可見時是不需要加載圖片的蜓堕,這里是有待優(yōu)化的地方博其。

  • INIT(初始化整圖)
    INIT狀態(tài)會去根據(jù)View的顯示區(qū)域加載一張完整的圖片,精度會根據(jù)圖片和顯示區(qū)域的尺寸比例計算出一個比較合適的值背伴,如果圖片很大儡率,精度就會比較低,如果圖片比較小儿普,精度就很高货徙,這張圖片是作為底圖使用。

  • FREE(ScaleType完成)
    圖片顯示都會有一個ScaleType浪汪,目前只有兩種,F(xiàn)IT_CENTER死遭,F(xiàn)IT_AUTO,前者和ImageView的一樣钉迷,后者的顯示方式可以參考微博圖片钠署,微信圖片的顯示方式,對于長圖會比較明顯谐鼎,只有這一步做完了才能真正的去顯示圖片,也就是FREE狀態(tài)才是用于顯示圖片的身害。圖片的縮放,移動傍菇,快速移動等操作都是在這個狀態(tài)上做的界赔。

內(nèi)存緩存

  • LruCache增強(qiáng)
    Android中的LruCache的使用和前面介紹的完全一致,這里使用的時候做了一些處理淮悼,實現(xiàn)了一個新的LruCache名為IntensifyCache,比如實現(xiàn)了create()见擦,在獲取Bitmap時羹令,如果不存在就加載等,并且添加了兩個方法酒来,一個是justGet()用于僅僅獲取肪凛,不會自動觸發(fā)create()方法,前面說過get()方法在獲取不到時會觸發(fā)create()方法去創(chuàng)建伟墙。另一個方法是alternative()用于提供備選方法,就是在get()獲取不到時先不創(chuàng)建就乓,而是先去查找已有的其他緩存是否有可代替的拱烁,如果可以找到,那么就返回這個守伸,否則繼續(xù)走create()方案浦妄,這里的設(shè)計是針對Bitmap緩存做的,不一定適合其他的緩存剂娄。
    上面介紹了兩個增加的方法的大概功能,現(xiàn)在介紹下IntensifyImageCache是如何緩存Bitmap的和二,首先IntensifyImageCache類有個內(nèi)部類ImageCache是根據(jù)Point緩存Bitmap的緩存類耳胎,這個類也是繼承IntensifyCache的。此類的兩個比較重要的方法實現(xiàn)如下:

    @Override
    protected Bitmap create(Point key) {
         BitmapFactory.Options options = new BitmapFactory.Options();
         options.inSampleSize = level;
         Rect rect = blockRect(key.x, key.y, BLOCK_SIZE);
         if (rect.intersect(mOriginalRect)) {
            return mRegionDecoder.decodeRegion(rect, options);
         }
         return null;
    }
    

    create方法废登,此方法是指定的點獲取對應(yīng)的Bitmap對象郁惜,這里所說的點并不是像素點,而是將圖片按照某個尺寸劃分出來的方格的坐標(biāo)兆蕉,后面會有詳細(xì)點的說明。這里其實不難明白易稠,其實就是加載指定坐標(biāo)對應(yīng)的Bitmap對象包蓝,level就是精度。只用與圖片原始區(qū)域有交集時才會加載并返回對應(yīng)的Bitmap對象衬吆。

    另外一個方法是alternative,此方法是提供備選绳泉,當(dāng)獲取某精度下的某坐標(biāo)的Bitmap對象時發(fā)現(xiàn)沒有逊抡,這時會優(yōu)先去尋找更高精度的對應(yīng)位置的緩存,一旦發(fā)現(xiàn)會直接返回并使用其零酪。代碼如下:

    @Override
    protected Bitmap alternative(Point key, Integer level) {
       if (!this.level.equals(level)) {
           Bitmap bitmap = justGet(key);
           if (bitmap != null) {
               return bitmap;
           }
       }
       if (level > 1) {
           ImageCache imageCache = IntensifyImageCache.this.justGet(level >> 1);
           if (imageCache != null) {
               return imageCache.alternative(key, level >> 1);
           }
       }
       return null;
    }
    

    這些都是ImageCache做的事情冒嫡,那么IntensifyImageCache做了什么呢?IntensifyImageCache是按照精度四苇,緩存了不同的ImageCache孝凌,它的鍵值是精度,值是ImageCache月腋,按照最開始說的蟀架,其sizeOf()方法要使用固定返回值瓣赂,這里使用的是默認(rèn)值1片拍。并不為其做備選策略煌集。

圖片表示

這個過程摸索了很久,最后確定用一個RectF對象捌省,始終表示著真正的圖片的邊界苫纤,需要計算顯示的就是與可視區(qū)域的交集部分,每次當(dāng)縮放纲缓,滑動等操作時都會去計算并修改RectF對象卷拘,之所以使用RectF而不使用Rect想必大家都明白,RectF是浮點型祝高,表示的更加精確恭金,防止圖像會有細(xì)微的跳躍感。如此一來我的滑動等操作褂策,都可以使用系統(tǒng)提供的ScrollBy等横腿,F(xiàn)ling也會更加簡單,顯示區(qū)域的真正區(qū)域是完全分開的斤寂,顯示時只需計算交集即可耿焊,另外也無需關(guān)心原點在哪里。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末遍搞,一起剝皮案震驚了整個濱河市罗侯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌溪猿,老刑警劉巖钩杰,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異诊县,居然都是意外死亡讲弄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門依痊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來避除,“玉大人,你說我怎么就攤上這事胸嘁∑堪冢” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵性宏,是天一觀的道長群井。 經(jīng)常有香客問我,道長毫胜,這世上最難降的妖魔是什么书斜? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任诬辈,我火速辦了婚禮,結(jié)果婚禮上菩佑,老公的妹妹穿的比我還像新娘。我一直安慰自己凝化,他們只是感情好稍坯,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搓劫,像睡著了一般瞧哟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上枪向,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天勤揩,我揣著相機(jī)與錄音,去河邊找鬼秘蛔。 笑死陨亡,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的深员。 我是一名探鬼主播负蠕,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼倦畅!你這毒婦竟也來了遮糖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤叠赐,失蹤者是張志新(化名)和其女友劉穎欲账,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芭概,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡赛不,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了罢洲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俄删。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖奏路,靈堂內(nèi)的尸體忽然破棺而出畴椰,到底是詐尸還是另有隱情,我是刑警寧澤鸽粉,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布斜脂,位于F島的核電站,受9級特大地震影響触机,放射性物質(zhì)發(fā)生泄漏帚戳。R本人自食惡果不足惜玷或,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望片任。 院中可真熱鬧偏友,春花似錦、人聲如沸对供。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽产场。三九已至鹅髓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間京景,已是汗流浹背窿冯。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留确徙,地道東北人醒串。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像鄙皇,于是被迫代替她去往敵國和親厦凤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容