官方文檔 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 . 圖像寬高與組件寬高比例 : 加載的圖像高度寬度 , 與組件的高度寬度比例一致 ;
3 . 縮放因子 : 由於寬度必須填充慢組件寬度 , 這裏需要縮放圖片 , 高分辨率手機需要縮小圖片 , 低分辨率手機需要放大圖片 ;
4 . 計算區域高度 : 圖像截取的寬度已知 , 組件的寬高已知 , 計算圖像截取的高度 :
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);
}
五、執行效果
豎屏效果 :
橫屏效果 :
六、源碼及資源下載
源碼及資源下載地址 :
-
① GitHub 工程地址 : Long_Graph_Loading
-
② LongImageView.java 主界面代碼地址 : LongImageView.java , 這是上述示自定義組件代碼 ;