Android加載長(zhǎng)圖

Android系統(tǒng)對(duì)加載圖片做了一些限制蛉艾,其中一個(gè)就是對(duì)Bitmap有最大寬高限制照棋,某些系統(tǒng)的機(jī)子的限制不一樣科贬,但起碼是4096x4096。當(dāng)加載一張長(zhǎng)或者寬鳖悠,或者兩者都超出限制的圖片的時(shí)候榜掌,通常會(huì)報(bào)出:Bitmap too large to be uploaded into a texture exception。圖片并不會(huì)顯示乘综。

那么憎账,怎么解決這個(gè)問(wèn)題呢?

首先思路是:既然這個(gè)限制是對(duì)寬高進(jìn)行限制的卡辰,而且限制值也知道了胞皱,那么只要控制在加載圖片前,將圖片的寬高都限制在4096以?xún)?nèi)九妈。

解決方案

1.壓縮圖片像素
將一張圖片的寬高都?jí)嚎s在4096以?xún)?nèi)反砌,Android提供了挺多可以這樣操作的API方法,譬如萌朱,指定尺寸進(jìn)行壓縮:

// 利用矩陣并指定寬高
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m)

 // 用法示例
 public static Bitmap resizeImage(Bitmap bitmap, int w, int h)  {  
        // 原圖的bitmap
        Bitmap BitmapOrg = bitmap;  
        // 原圖的寬高
        int width = BitmapOrg.getWidth();  
        int height = BitmapOrg.getHeight();  
        // 指定的新的寬高
        int newWidth = w;  
        int newHeight = h;  
        // 計(jì)算的縮放比例
        float scaleWidth = ((float) newWidth) / width;  
        float scaleHeight = ((float) newHeight) / height;  
        // 用于縮放的矩陣
        Matrix matrix = new Matrix();  
        // 矩陣縮放
        matrix.postScale(scaleWidth, scaleHeight);  
        // 如果要旋轉(zhuǎn)圖片 
        // matrix.postRotate(45);   
        // 生成新的bitmap
        Bitmap resizedBitmap = Bitmap.createBitmap(BitmapOrg, 0, 0, width,  
                        height, matrix, true);  
        return resizedBitmap;  
    } 

當(dāng)然宴树,createBitmap(...)還有多種方法,不使用矩陣的而直接縮放的也有createScaledBitmap(...)晶疼,但用法基本一樣酒贬,最后也是生成一個(gè)新的Bitmap對(duì)象,尺寸可以直接指定翠霍,或者通過(guò)某些計(jì)算規(guī)則(如屏幕寬高與原圖分辨率的比值等)獲得尺寸锭吨,只要保證這個(gè)尺寸是長(zhǎng)寬小于等于4096的就可以成功加載。

同樣的寒匙,不加載超規(guī)格的原圖零如,而是加載調(diào)整過(guò)尺寸的縮略圖,也是同樣的道理,不過(guò)使用的API方法不一樣埠况,下面也給一個(gè)示例:

    //使用BitmapFactory.Options的inSampleSize參數(shù)來(lái)縮放
    public static Bitmap resizeImage2(String path, int width, int height) {
        // Options 代表圖片在解析的時(shí)候耸携,BitmapFactory內(nèi)部應(yīng)該如何處理圖片內(nèi)容
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 不加載bitmap到內(nèi)存中
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path,options); 
        // 原圖寬高
        int outWidth = options.outWidth;
        int outHeight = options.outHeight;
        options.inDither = false;
        // 圖片格式,當(dāng)然為了省內(nèi)存也可以用RGB_565
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        options.inSampleSize = 1;
         
        if (outWidth != 0 && outHeight != 0 && width != 0 && height != 0) 
        {
            // 圖片解碼采樣2的冪辕翰,采樣效率是最快的
            int sampleSize = (outWidth/width+outHeight/height)/2;
            options.inSampleSize = sampleSize;
        }
     
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(path, options);     
    }

這兩種方法可以解決超規(guī)格的大圖夺衍、長(zhǎng)圖的加載問(wèn)題,不過(guò)圖片經(jīng)過(guò)壓縮喜命,分辨率的降低沟沙,圖片的清晰度肯定是有損失的。

按照解決問(wèn)題的思路壁榕,同時(shí)又不想圖片的質(zhì)量下降矛紫,那么還有什么方法呢?

2.圖片分割
實(shí)際上牌里,這只是為了方便理解颊咬,準(zhǔn)確地說(shuō)應(yīng)該是圖片區(qū)域解碼,就是解碼圖片的一塊不大于4096X4096的區(qū)域?yàn)锽itmap再顯示牡辽。
圖片不壓縮喳篇,而是顯示圖片局部區(qū)域,相當(dāng)于將圖片“分割”成一塊塊地去加載态辛、顯示麸澜。

Android里面提供了這樣的一個(gè)類(lèi),BitmapRegionDecoder奏黑。

/**
 * BitmapRegionDecoder can be used to decode a rectangle region from an image.
 * BitmapRegionDecoder is particularly useful when an original image is large and
 * you only need parts of the image.
 *
 * <p>To create a BitmapRegionDecoder, call newInstance(...).
 * Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly
 * to get a decoded Bitmap of the specified region.
 *
 */

看源碼里面對(duì)這個(gè)類(lèi)的描述就可以知道:BitmapRegionDecoder類(lèi)用來(lái)編譯(解碼)在圖片內(nèi)不同的方形區(qū)域炊邦,BitmapRegionDecoder類(lèi)在使用較大圖片只需要取得圖片中的一小部分的內(nèi)容是特別有效益的。

那么就看一下熟史,怎么用這個(gè)類(lèi)去加載超規(guī)格的圖片馁害。
主要有四步:
① 使用BitmapRegionDecoder的newInstance(...)獲得一個(gè)解碼對(duì)象decoder;
② 生成一個(gè)Rect類(lèi)的對(duì)象rect蹂匹,這個(gè)矩形對(duì)象就是設(shè)置好要解碼的區(qū)域(當(dāng)然長(zhǎng)寬不能大于4096蜗细,實(shí)際上就算設(shè)置了大于4096的數(shù)值,最后也是會(huì)不能加載的)怒详;
③ decoder調(diào)用decodeRegion(...)將rect對(duì)象轉(zhuǎn)化為bitmap對(duì)象炉媒;
④ 控件加載bitmap。

同樣來(lái)一個(gè)示例:

        dm = new DisplayMetrics();  
        getWindowManager().getDefaultDisplay().getMetrics(dm);  
        screenHeight = dm.heightPixels;  
        screenWidth = dm.widthPixels;  
        ...
        try { 
            // 獲取本地圖片 
            InputStream is = getResources().openRawResource(R.drawable.bg);  
            // 生成decoder對(duì)象
            mDecoder = BitmapRegionDecoder.newInstance(is, true);  
  
            final int imgWidth = mDecoder.getWidth();  
            final int imgHeight = mDecoder.getHeight();  
           
            BitmapFactory.Options opts = new BitmapFactory.Options();  
            // 為了內(nèi)存考慮昆烁,將圖片格式轉(zhuǎn)化為RGB_565
             opts .inPreferredConfig = Bitmap.Config.RGB_565;

            // 設(shè)置圖片解碼矩形區(qū)域
            mRect.set(0, 0, 2000, 3000);  
            // 將矩形區(qū)域解碼生成要加載的Bitmap對(duì)象
            Bitmap bm = mDecoder.decodeRegion(mRect, opts);  
            // 控件加載Bitmap對(duì)象
            img.setImageBitmap(bm);  
            ...
        } catch (IOException e) {  
            e.printStackTrace();  
        }  

使用起來(lái)并不困難吊骤,而且效果不錯(cuò),當(dāng)然也要考慮內(nèi)存而做一些處理静尼,而且白粉,這里只是用一個(gè)控件加載了圖片的一部分區(qū)域传泊,可以看到Rect對(duì)象調(diào)用set(...)可以設(shè)置區(qū)域大小,那么計(jì)算圖片的寬高鸭巴,“分割”成幾個(gè)部分給多個(gè)控件去分別加載眷细,這些控件無(wú)間距布局在一起,就相當(dāng)于整個(gè)圖片完整顯示出來(lái)了鹃祖。至于怎么計(jì)算要多少個(gè)控件溪椎,“分割”的區(qū)域大小多少合適?這里就不展開(kāi)說(shuō)了恬口。同樣的基于這個(gè)原理校读,也可以自定義View做成在一個(gè)控件里面清晰顯示整張圖片,具體的可以看鴻洋大神的這篇博客:Android 高清加載巨圖方案 拒絕壓縮圖片

注意:如果讀取的長(zhǎng)圖是本地圖片祖能,那么放在drawable或者mipmap歉秫,是無(wú)法生成資源ID的,也就是無(wú)法使用的养铸,可以放在raw或者assets文件夾中雁芙。但怎么獲取這些文件夾里面的圖片這里就不展開(kāi)說(shuō)了。

如果還是嫌上面的兩個(gè)方案做起來(lái)麻煩钞螟,或者還想要加上手勢(shì)縮放却特,平移等,GitHub上面也有一些開(kāi)源的庫(kù)可以使用筛圆,比較流行的是subsampling-scale-image-view,可以看一看椿浓,試一試太援。

Android加載長(zhǎng)圖暫時(shí)就這么多,歡迎多多交流扳碍。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末提岔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子笋敞,更是在濱河造成了極大的恐慌碱蒙,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夯巷,死亡現(xiàn)場(chǎng)離奇詭異赛惩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)趁餐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)喷兼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人后雷,你說(shuō)我怎么就攤上這事季惯》透鳎” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵勉抓,是天一觀(guān)的道長(zhǎng)贾漏。 經(jīng)常有香客問(wèn)我,道長(zhǎng)藕筋,這世上最難降的妖魔是什么纵散? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮念逞,結(jié)果婚禮上困食,老公的妹妹穿的比我還像新娘。我一直安慰自己翎承,他們只是感情好硕盹,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著叨咖,像睡著了一般瘩例。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上甸各,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天垛贤,我揣著相機(jī)與錄音,去河邊找鬼趣倾。 笑死聘惦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的儒恋。 我是一名探鬼主播善绎,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诫尽!你這毒婦竟也來(lái)了禀酱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤牧嫉,失蹤者是張志新(化名)和其女友劉穎剂跟,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體酣藻,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡曹洽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辽剧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衣洁。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖抖仅,靈堂內(nèi)的尸體忽然破棺而出坊夫,到底是詐尸還是另有隱情砖第,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布环凿,位于F島的核電站梧兼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏智听。R本人自食惡果不足惜羽杰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望到推。 院中可真熱鬧考赛,春花似錦、人聲如沸莉测。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)捣卤。三九已至忍抽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間董朝,已是汗流浹背鸠项。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留子姜,地道東北人祟绊。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像哥捕,于是被迫代替她去往敵國(guó)和親牧抽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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