Android加載長圖那些事

首先我們來創建兩個角色,大神A和菜鳥B。
B:“A神,快來幫我看個問題,太奇怪了。爲難死寶寶了0.0”
A:“怎麼了,說來聽聽”
B:“是這樣的,我的一個頁面需要加載一張背景圖。圖的高度和屏幕高度相同,但是寬度是屏幕寬度的5倍,然後我把它放到一個HorizontalScrollView中,想通過可滑動來展示全這張圖片。結果屏幕白茫茫一片,什麼都不顯示,也沒報錯,我都哭了。”
A:“先別急,你覺得可能是哪方面問題呢”
B:“我覺得可能是兩方面的問題:1.圖片質量太大;2.圖片太長
A:“那你有沒有試着弄清楚到底是哪裏的問題呢?”
B:“試了,我又試着加載了一張長寬都小於手機尺寸的5M大小的圖(長圖2.5M),結果可以完全顯示。,然後又去找了一些關於加載長圖的資料,但還是沒弄明白。”
A:“嗯,你的思路大概是對的。是這樣的,在Android中,解析圖片時,當圖片高度或寬度超過閥值時,會解析失敗。你可以去看一下BitmapRegionDecoder,說不定會有什麼發現哦。”
接下來,是B同學的整理。

場景

對於圖片加載有一種這樣的情況,就是單個圖片非常巨大,並且還不允許壓縮。比如顯示:世界地圖、微博長圖等。首先不壓縮,按照原圖尺寸加載,那麼屏幕肯定是不夠大的,並且考慮到內存的情況,不可能一次性整圖加載到內存中,所以肯定是局部加載。這就需要用到Api提供的這個類:BitmapRegionDecoder。

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.

To create a BitmapRegionDecoder, call newInstance(...). Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly to get a decoded Bitmap of the specified region.

BitmapRegionDecoder主要用於顯示圖片的某一塊矩形區域,如果你需要顯示某個圖片的指定區域,或者你需要顯示一張非常大的圖片,那麼這個類非常合適。我們可以調用newInstance(…)方法來獲得一個BitmapRegionDecoder對象,然後調用decodeRegion()方法就可以獲得指定位置的圖片了。
接下來看一個簡單的例子

package oracleen.decoder;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Rect;
import android.os.Bundle;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends Activity {

    private ImageView decoder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        decoder = (ImageView) findViewById(R.id.decoder);

        try {
            InputStream inputStreamF = getAssets().open("datu.png");

            //獲得圖片的寬、高
            BitmapFactory.Options tmpOptions = new BitmapFactory.Options();
            tmpOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(inputStreamF, null, tmpOptions);
            int width = tmpOptions.outWidth;
            int height = tmpOptions.outHeight;

            InputStream inputStreamS = getAssets().open("datu.png");
            //設置顯示圖片的中心區域
            BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStreamS, false);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(0, 0, width / 2, height), options);
            decoder.setImageBitmap(bitmap);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

這裏我們截取了一個高度等於大圖高度,長度等於大圖長度一般的圖片。

這裏注意一下,有時候會拋

Throws
IOException if the image format is not supported or can not be decoded.

這個異常,這裏需要重新初始化一下我們的輸入流,保證它是初始的可讀狀態。

這裏寫圖片描述

小B的總結

對於小B的問題有兩種解決辦法:
1.把原始的長圖切成5張小圖,分別設置給5個ImageView,這樣每個imageview加載的圖都不會超出閾值。
但是問題是整張圖會全部加載出來,可能會出現OOM。
2.重寫HorizontalScrollView,根據左右滑動的距離用BitmapRegionDecoder去分區域顯示。
3.自定義view,在view內部用BitmapRegionDecoder去分區域顯示。然後對外提供一個設置圖片資源的方法。

參考鏈接:
http://blog.csdn.net/lmj623565791/article/details/49300989

http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html#newInstance%28java.io.InputStream,%20boolean%29

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章