【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{加載的圖像寬度}{加載的圖像高度}mViewHeightmViewWidth?=加載的圖像高度加載的圖像寬度?
mViewWidth加載的圖像寬度=mViewHeight加載的圖像高度\dfrac{mViewWidth }{加載的圖像寬度} = \dfrac{mViewHeight }{加載的圖像高度}加載的圖像寬度mViewWidth?=加載的圖像高度mViewHeight?
3 . 縮放因子 : 由于寬度必須填充慢組件寬度 , 這里需要縮放圖片 , 高分辨率手機需要縮小圖片 , 低分辨率手機需要放大圖片 ;
縮放因子=mViewWidth加載的圖像寬度縮放因子 = \dfrac{mViewWidth}{加載的圖像寬度 }縮放因子=加載的圖像寬度mViewWidth?
4 . 計算區域高度 : 圖像截取的寬度已知 , 組件的寬高已知 , 計算圖像截取的高度 :
mViewWidth加載的圖像寬度=mViewHeight加載的圖像高度=mViewHeight×加載的圖像寬度mViewWidth=mViewHeight縮放因子\begin{array}{lcl} \dfrac{mViewWidth }{加載的圖像寬度} &=& \dfrac{mViewHeight }{加載的圖像高度} \\\\ &=& \dfrac{mViewHeight \times 加載的圖像寬度}{mViewWidth} \\\\ &=& \dfrac{mViewHeight }{縮放因子} \end{array}加載的圖像寬度mViewWidth??===?加載的圖像高度mViewHeight?mViewWidthmViewHeight×加載的圖像寬度?縮放因子mViewHeight??
5 . 代碼示例 : 在 onMeasure 方法中 , 獲取最新測量出來的組件寬高 , 根據以上公式 , 計算出要解碼圖像的寬高 ;
@Overrideprotected 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();}// ...@Overrideprotected 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 , 這是上述示自定義組件代碼 ;
總結
以上是生活随笔為你收集整理的【Android 内存优化】自定义组件长图组件 ( 获取图像宽高 | 计算解码区域 | 设置图像解码属性 复用 像素格式 | 图像绘制 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 内存优化】自定义组件长
- 下一篇: 【Android 内存优化】自定义组件长