Android大圖加載

最近公司一個需求拴竹,加載一張超大圖悟衩,允許手勢放大縮小以及左右滑動查看。第一想法是利用inSampleSize縮小圖片分辨率栓拜,但是效果不大理想座泳,因為放大后圖片會變模糊。產(chǎn)品要求放大顯示時幕与,圖片清晰度不能降低挑势,查了下Btimap相關(guān)的api,發(fā)現(xiàn)BitmapRegionDecoder能夠滿足需求啦鸣。

BitmapRegionDecoder

BitmapRegionDecoder.png

根據(jù)官方文檔說明潮饱,當(dāng)原始圖片很大但只想顯示原始圖片的一部分時,可以使用
decodeRegion.png

方案找到了诫给,直接搞起
首先解析圖片香拉,計算出圖片完全展示時的壓縮比例,并初始化BitmapRegionDecoder

    public void setImageFile(File file){
        try {
            InputStream inputStream = new FileInputStream(file);
            //獲得圖片的寬中狂、高
            BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
            tmpOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(inputStream, null, tmpOptions);
            srcWidth = tmpOptions.outWidth;  //原始圖片寬度
            srcHeight = tmpOptions.outHeight; //原始圖片高度

            int ivWidth = getMeasuredWidth(); //顯示區(qū)域?qū)挾?            int ivHeight = getMeasuredHeight();  //顯示區(qū)域高度
            inSampleSize = 1;  //默認不壓縮
            //下面為計算初始狀態(tài)凫碌,圖片完全展示時的壓縮比例
            if(ivWidth > 0 && ivHeight > 0){
                if (srcHeight > srcWidth && srcHeight>ivHeight) {
                    while (srcHeight/inSampleSize > ivHeight){
                        inSampleSize = inSampleSize * 2;
                    }
                }
                else if(srcHeight<=srcWidth && srcWidth>ivWidth){
                    while (srcWidth/inSampleSize > ivWidth){
                        inSampleSize = inSampleSize * 2;
                    }
                }
            }
            //BitmapRegionDecoder創(chuàng)建
            bitmapRegionDecoder = BitmapRegionDecoder.newInstance(file.getAbsolutePath(), false);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

手勢操作代碼忽略不寫,直接上繪制圖片的代碼吃型。
重寫onDraw方法证鸥,注意super.onDraw(canvas);需要刪掉

    @Override
    protected void onDraw(Canvas canvas) {
        int width = getMeasuredWidth(); //顯示的寬度,也就是當(dāng)前顯示View的寬度
        int height = getMeasuredHeight(); //同上
        //注:rectF為手勢縮放以及平移后,整個圖片的矩陣位置枉层,具體邏輯就不貼出來了
        int scale = (int) (rectF.width() / width);  //圖片手勢縮放的比例
        //下面為根據(jù)當(dāng)前手勢縮放比例泉褐,重新計算圖片繪制時的壓縮比例
        int currentSampleSize = inSampleSize;
        while (scale/2 >= 1){
            currentSampleSize = currentSampleSize/2;
            scale = currentSampleSize / 2;
        }
        if(currentSampleSize < 1){
            currentSampleSize = 1;
        }
        options.inSampleSize = currentSampleSize;

        int left = (int) (-rectF.left/rectF.width() * srcWidth);
        int top = (int) (-rectF.top/rectF.height() * srcHeight);
        int right = (int) ((width - rectF.left)/rectF.width() *srcWidth);
        int bottom = (int) ((height - rectF.top)/rectF.height() * srcHeight);
        destRect.set(0, 0, width, height); //屏幕的繪制區(qū)域,即當(dāng)前顯示View的整個大小
        if(bitmapRegionDecoder != null){
            drawRect.set(left, top, right, bottom); //原始圖片需要截取顯示的區(qū)域
            Bitmap bitmap = bitmapRegionDecoder.decodeRegion(drawRect, options); //截取需要顯示的圖片區(qū)域
            canvas.drawBitmap(bitmap, null, destRect, null);  //繪制
        }
    }
發(fā)現(xiàn)問題

查看運行效果鸟蜡,發(fā)現(xiàn)放大后圖片未模糊膜赃,問題解決。但是引發(fā)一個新的問題揉忘,縮放或滑動的時候會比較卡頓跳座。分析了下,原來是在onDraw的時候頻繁創(chuàng)建Bitmap導(dǎo)致泣矛。

Bitmap bitmap = bitmapRegionDecoder.decodeRegion(drawRect, options); //截取需要顯示的圖片區(qū)域
解決問題

方案一
考慮創(chuàng)建Bitmap時疲眷,將Bitmap創(chuàng)建比顯示區(qū)域稍微大一些,這樣您朽,在小幅度滑動或縮放時狂丝,就不必重新創(chuàng)建Bitmap。通過減少Bitmap創(chuàng)建次數(shù)哗总,降低卡頓几颜。
如下圖,黃色矩形為顯示區(qū)域讯屈,紅色矩形為截取的圖片蛋哭,在小幅度滑動或縮放時,圖片依然可以正常顯示涮母,不用重新創(chuàng)建谆趾。


1.png
    @Override
    protected void onDraw(Canvas canvas) {
        
        ...

        if(lastSampleSize == currentSampleSize && lastDrawBitmap != null && isHitRect(drawRect, lastBitmapRect)){
            //當(dāng)圖片像素壓縮比例未變,且上次創(chuàng)建的圖片依然可以覆蓋顯示區(qū)域時叛本,直接使用上次緩存的Bitmap
            float bitmapScale = (float)(lastDrawBitmap.getWidth()) / lastBitmapRect.width();
            int srcLeft = (int) ((drawRect.left-lastBitmapRect.left) * bitmapScale);
            int srcTop = (int) ((drawRect.top-lastBitmapRect.top) * bitmapScale);
            int srcRight = (int) ((drawRect.right-lastBitmapRect.left) * bitmapScale);
            int srcBottom = (int) ((drawRect.bottom-lastBitmapRect.top) * bitmapScale);
            srcRect.set(srcLeft, srcTop, srcRight, srcBottom);
            canvas.drawBitmap(lastDrawBitmap, srcRect, destRect, null);
        } else if(bitmapRegionDecoder != null){
            //創(chuàng)建Bitmap棺妓,左右多截取1/4冗余
            lastSampleSize = currentSampleSize;
            lastBitmapRect.set(left-drawRect.width()/4, top-drawRect.height()/4, right+drawRect.width()/4, bottom+drawRect.height()/4);
//            lastBitmapRect.set(left, top, right, bottom);
            lastDrawBitmap = bitmapRegionDecoder.decodeRegion(lastBitmapRect, options);
            float bitmapScale = (float)(lastDrawBitmap.getWidth()) / lastBitmapRect.width();
            int srcLeft = (int) ((drawRect.left-lastBitmapRect.left) * bitmapScale);
            int srcTop = (int) ((drawRect.top-lastBitmapRect.top) * bitmapScale);
            int srcRight = (int) ((drawRect.right-lastBitmapRect.left) * bitmapScale);
            int srcBottom = (int) ((drawRect.bottom-lastBitmapRect.top) * bitmapScale);
            srcRect.set(srcLeft, srcTop, srcRight, srcBottom);
            canvas.drawBitmap(lastDrawBitmap, srcRect, destRect, null);
        }
    }

運行后,效果不錯炮赦,整體流暢怜跑,只在重新創(chuàng)建Bitmap時卡頓一下,可以接受吠勘。

方案二
將圖片分割為一個個小矩形性芬,在滑動或縮放后,在緩存中查找顯示區(qū)域?qū)?yīng)的小矩形Bitmap剧防,如果緩存中沒有植锉,直接使用模糊版的Bitmap占位,再在子線程中創(chuàng)建此時需要的小矩形bitmap峭拘,創(chuàng)建完成后俊庇,緩存起來狮暑,然后將其刷新到畫面上。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辉饱,一起剝皮案震驚了整個濱河市搬男,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌彭沼,老刑警劉巖缔逛,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異姓惑,居然都是意外死亡褐奴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門于毙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敦冬,“玉大人,你說我怎么就攤上這事唯沮》瞬梗” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵烂翰,是天一觀的道長。 經(jīng)常有香客問我蚤氏,道長甘耿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任竿滨,我火速辦了婚禮佳恬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘于游。我一直安慰自己毁葱,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布贰剥。 她就那樣靜靜地躺著倾剿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蚌成。 梳的紋絲不亂的頭發(fā)上前痘,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音担忧,去河邊找鬼芹缔。 笑死,一個胖子當(dāng)著我的面吹牛瓶盛,可吹牛的內(nèi)容都是我干的最欠。 我是一名探鬼主播示罗,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼芝硬!你這毒婦竟也來了蚜点?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤吵取,失蹤者是張志新(化名)和其女友劉穎禽额,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體皮官,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡脯倒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了捺氢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片藻丢。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖摄乒,靈堂內(nèi)的尸體忽然破棺而出悠反,到底是詐尸還是另有隱情,我是刑警寧澤馍佑,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布斋否,位于F島的核電站,受9級特大地震影響拭荤,放射性物質(zhì)發(fā)生泄漏茵臭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一舅世、第九天 我趴在偏房一處隱蔽的房頂上張望旦委。 院中可真熱鬧,春花似錦雏亚、人聲如沸缨硝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽查辩。三九已至,卻和暖如春网持,著一層夾襖步出監(jiān)牢的瞬間宜肉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工翎碑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谬返,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓日杈,卻偏偏與公主長得像遣铝,于是被迫代替她去往敵國和親佑刷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353