【Android 內存優化】自定義組件長圖組件 ( 獲取圖像寬高 | 計算解碼區域 | 設置圖像解碼屬性 複用 像素格式 | 圖像繪製 )



官方文檔 API : BitmapRegionDecoder


【Android 內存優化】自定義組件長圖組件 ( 自定義組件構造方法 ) 基礎上繼續開發 ;





一、獲取圖像真實寬高



顯示的圖像是一張長圖 , 在該組件中 , 寬度肯定要完整顯示出來 , 解碼圖片的不同高度的數據 ;

首先要測量圖片數據的真實寬高 , 然後根據圖像的寬高 , 與組件的寬高 , 以及要顯示的圖像位置 , 計算要解碼的圖像區域 ;


參考 【Android 內存優化】Bitmap 圖像尺寸縮小 ( 設置 Options 參數 | inJustDecodeBounds | inSampleSize | 工具類實現 ) 一、解碼圖片參數 inJustDecodeBounds 章節內容 , 有圖片解碼的詳細步驟 ;



1 . 圖片尺寸數據解碼 :


① 創建解碼選項 : 創建 BitmapFactory.Options 對象 ;

② 設置解碼尺寸數據 : 設置 BitmapFactory.Options 對象的 inJustDecodeBounds 爲 true , 解碼圖像時 , 不解碼圖像數據 , 只獲取圖像的尺寸數據 ;

③ 解碼圖像尺寸數據 : 調用 BitmapFactory.decodeStream 方法 , 解碼圖片 , 圖片相關的尺寸數據保存到了 mOptions 選項中 ;

④ 獲取圖片尺寸 : mOptions.outWidth 是解碼出的圖像寬度 , mOptions.outHeight 是解碼出的圖像高度 ;



2 . 代碼示例 :

/**
 * Bitmap 解碼選項
 */
private BitmapFactory.Options mOptions;

/**
 * 圖片寬度
 */
private int mImageWidth;

/**
 * 圖片高度
 */
private int mImageHeight;

// ...

// 解碼選項
mOptions = new BitmapFactory.Options();
// 讀取圖片的尺寸數據
mOptions.inJustDecodeBounds = true;
// 解碼圖片 , 圖片相關的尺寸數據保存到了 mOptions 選項中
BitmapFactory.decodeStream(inputStream, null, mOptions);
// 獲取圖片寬高
mImageWidth = mOptions.outWidth;
mImageHeight = mOptions.outHeight;




二、計算解碼區域



1 . 顯示區域計算原則 : 這是一張長圖 , 寬度完全顯示 , 高度顯示部分 ; 根據組件的寬高計算圖像顯示的區域 , 組件的寬高已知 , 寬高比例確定 ; 該寬高比例下 , 圖片顯示的區域也必須是該比例 ;



2 . 圖像寬高與組件寬高比例 : 加載的圖像高度寬度 , 與組件的高度寬度比例一致 ;


mViewWidthmViewHeight=\dfrac{mViewWidth }{mViewHeight} = \dfrac{加載的圖像寬度}{加載的圖像高度}


mViewWidth=mViewHeight\dfrac{mViewWidth }{加載的圖像寬度} = \dfrac{mViewHeight }{加載的圖像高度}



3 . 縮放因子 : 由於寬度必須填充慢組件寬度 , 這裏需要縮放圖片 , 高分辨率手機需要縮小圖片 , 低分辨率手機需要放大圖片 ;

=mViewWidth縮放因子 = \dfrac{mViewWidth}{加載的圖像寬度 }



4 . 計算區域高度 : 圖像截取的寬度已知 , 組件的寬高已知 , 計算圖像截取的高度 :

mViewWidth=mViewHeight=mViewHeight×mViewWidth=mViewHeight\begin{array}{lcl} \dfrac{mViewWidth }{加載的圖像寬度} &=& \dfrac{mViewHeight }{加載的圖像高度} \\\\ &=& \dfrac{mViewHeight \times 加載的圖像寬度}{mViewWidth} \\\\ &=& \dfrac{mViewHeight }{縮放因子} \end{array}



5 . 代碼示例 : 在 onMeasure 方法中 , 獲取最新測量出來的組件寬高 , 根據以上公式 , 計算出要解碼圖像的寬高 ;

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 獲取測量的自定義 View 組件寬高
        mViewWidth = getMeasuredWidth();
        mViewHeight = getMeasuredHeight();

        // 根據組件的寬高 , 確定要加載的圖像的寬高
        if(mBitmapRegionDecoder != null){
            mRect.left = 0;
            mRect.top = 0;

            // 繪製的寬度就是圖像的寬度
            mRect.right = mImageWidth;

            // 根據圖像寬度 和 組件寬度 , 計算出縮放比例
            // 組件寬度 / 圖像寬度 = 縮放因子
            mScale = (float)mViewWidth / (float)mImageWidth;

            /*
                加載的圖像高度寬度 , 與組件的高度寬度比例一致
                mViewWidth / 加載的圖像寬度 = mViewHeight / 加載的圖像高度
                此處加載的圖像寬度就是實際的寬度

                 加載的圖像高度 = mViewHeight / ( mViewWidth / 加載的圖像寬度 )
                 mViewWidth / 加載的圖像寬度 就是縮放因子
                 加載的圖像高度 = mViewHeight / 縮放因子
             */

            // 根據縮放因子計算解碼高度
            mRect.bottom = (int) (mViewHeight / mScale);
        }
    }




三、設置解碼參數 內存複用 像素格式



設置圖像解碼參數 :


① 關閉尺寸解碼 : 之前解碼圖像尺寸 , 將 BitmapFactory.Options 的 inJustDecodeBounds 屬性設置爲了 true , 現在要開始解碼圖像數據了 , 需要關閉該選項 , 設置爲 false ;

② 設置像素格式 : 如果不需要顯示透明度 , 就設置 BitmapFactory.Options 的 inPreferredConfig 像素格式爲 Bitmap.Config.RGB_565 , 該像素格式每個像素佔 2 字節內存 ;

③ 設置可變 : 這是內存複用生效的前提 , 設置 inMutable 爲 true ;

④ 設置複用內存的 Bitmap 對象 : 每次解碼操作前都要設置一次 , 解碼時會複用該 Bitmap 中的內存 ;



2 . 代碼示例 :

/**
 * Bitmap 解碼選項
 */
private BitmapFactory.Options mOptions;

// ... 

// 設置 Bitmap 內存複用
mOptions.inMutable = true;  // 設置可變
// 內存複用
mOptions.inBitmap = mBitmap;
mOptions.inPreferredConfig = Bitmap.Config.RGB_565; // 設置像素格式 RGB 565
mOptions.inJustDecodeBounds = false; // 讀取完畢之後, 就需要解析實際的 Bitmap 圖像數據了




四、圖像繪製



1 . 圖像繪製 :


① 設置圖像區域解碼器 : 在爲自定義組件設置圖片時 , 設置區域解碼器 , 因爲要設置區域解碼的數據源 , 因此必須在用戶設置圖片時 , 纔可以創建區域解碼器 ;

② 設置內存複用 : 每次解碼時 , 都要設置一下內存複用的 Bitmap 對象 ; mOptions.inBitmap = mBitmap;

③ 解碼圖片 : 調用區域解碼器的 mBitmapRegionDecoder.decodeRegion 方法 , 解碼圖片的特定區域 ;

④ 設置圖片縮放 : 使用 Matrix 進行圖像縮放 ; 圖像與自定義組件的尺寸不同 , 因此需要將解碼區域完全填充到自定義組件中顯示 ;

⑤ 圖像繪製 : 調用 canvas.drawBitmap 繪製圖像 , 如果需要縮放 , 傳入 Matrix 參數 ;



2 . 代碼示例 :

    /**
     * 圖像區域解碼器
     */
    private BitmapRegionDecoder mBitmapRegionDecoder;

	// ...

    /**
     * 設置顯示的圖片
     * @param inputStream
     */
    public void setImage(InputStream inputStream){
        // ...

        try {
            // Bitmap 區域解碼器
            mBitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 設置圖片完畢後 , 刷新自定義組件
        requestLayout();
    }

	// ...
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(mBitmapRegionDecoder == null) return;

        // 內存複用
        mOptions.inBitmap = mBitmap;
        // 解碼圖片
        mBitmap = mBitmapRegionDecoder.decodeRegion(mRect, mOptions);

        // 設置繪製的圖像縮放 , x 軸和 y 軸都在 Bitmap 大小的區域基礎上 , 縮放 mScale 倍
        Matrix matrix = new Matrix();
        matrix.setScale(mScale, mScale);

        canvas.drawBitmap(mBitmap, matrix, null);
    }




五、執行效果



豎屏效果 :

在這裏插入圖片描述

橫屏效果 :

在這裏插入圖片描述





六、源碼及資源下載



源碼及資源下載地址 :

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