【Android 内存优化】自定义组件长图组件 ( 获取图像宽高 | 计算解码区域 | 设置图像解码属性 复用 像素格式 | 图像绘制 )
文章目錄
- 一、獲取圖像真實(shí)寬高
- 二、計(jì)算解碼區(qū)域
- 三、設(shè)置解碼參數(shù) 內(nèi)存復(fù)用 像素格式
- 四、圖像繪制
- 五、執(zhí)行效果
- 六、源碼及資源下載
官方文檔 API : BitmapRegionDecoder
在【Android 內(nèi)存優(yōu)化】自定義組件長(zhǎng)圖組件 ( 自定義組件構(gòu)造方法 ) 基礎(chǔ)上繼續(xù)開發(fā) ;
一、獲取圖像真實(shí)寬高
顯示的圖像是一張長(zhǎng)圖 , 在該組件中 , 寬度肯定要完整顯示出來 , 解碼圖片的不同高度的數(shù)據(jù) ;
首先要測(cè)量圖片數(shù)據(jù)的真實(shí)寬高 , 然后根據(jù)圖像的寬高 , 與組件的寬高 , 以及要顯示的圖像位置 , 計(jì)算要解碼的圖像區(qū)域 ;
參考 【Android 內(nèi)存優(yōu)化】Bitmap 圖像尺寸縮小 ( 設(shè)置 Options 參數(shù) | inJustDecodeBounds | inSampleSize | 工具類實(shí)現(xiàn) ) 一、解碼圖片參數(shù) inJustDecodeBounds 章節(jié)內(nèi)容 , 有圖片解碼的詳細(xì)步驟 ;
1 . 圖片尺寸數(shù)據(jù)解碼 :
① 創(chuàng)建解碼選項(xiàng) : 創(chuàng)建 BitmapFactory.Options 對(duì)象 ;
② 設(shè)置解碼尺寸數(shù)據(jù) : 設(shè)置 BitmapFactory.Options 對(duì)象的 inJustDecodeBounds 為 true , 解碼圖像時(shí) , 不解碼圖像數(shù)據(jù) , 只獲取圖像的尺寸數(shù)據(jù) ;
③ 解碼圖像尺寸數(shù)據(jù) : 調(diào)用 BitmapFactory.decodeStream 方法 , 解碼圖片 , 圖片相關(guān)的尺寸數(shù)據(jù)保存到了 mOptions 選項(xiàng)中 ;
④ 獲取圖片尺寸 : mOptions.outWidth 是解碼出的圖像寬度 , mOptions.outHeight 是解碼出的圖像高度 ;
2 . 代碼示例 :
/*** Bitmap 解碼選項(xiàng)*/ private BitmapFactory.Options mOptions;/*** 圖片寬度*/ private int mImageWidth;/*** 圖片高度*/ private int mImageHeight;// ...// 解碼選項(xiàng) mOptions = new BitmapFactory.Options(); // 讀取圖片的尺寸數(shù)據(jù) mOptions.inJustDecodeBounds = true; // 解碼圖片 , 圖片相關(guān)的尺寸數(shù)據(jù)保存到了 mOptions 選項(xiàng)中 BitmapFactory.decodeStream(inputStream, null, mOptions); // 獲取圖片寬高 mImageWidth = mOptions.outWidth; mImageHeight = mOptions.outHeight;二、計(jì)算解碼區(qū)域
1 . 顯示區(qū)域計(jì)算原則 : 這是一張長(zhǎng)圖 , 寬度完全顯示 , 高度顯示部分 ; 根據(jù)組件的寬高計(jì)算圖像顯示的區(qū)域 , 組件的寬高已知 , 寬高比例確定 ; 該寬高比例下 , 圖片顯示的區(qū)域也必須是該比例 ;
2 . 圖像寬高與組件寬高比例 : 加載的圖像高度寬度 , 與組件的高度寬度比例一致 ;
mViewWidthmViewHeight=加載的圖像寬度加載的圖像高度\dfrac{mViewWidth }{mViewHeight} = \dfrac{加載的圖像寬度}{加載的圖像高度}mViewHeightmViewWidth?=加載的圖像高度加載的圖像寬度?
mViewWidth加載的圖像寬度=mViewHeight加載的圖像高度\dfrac{mViewWidth }{加載的圖像寬度} = \dfrac{mViewHeight }{加載的圖像高度}加載的圖像寬度mViewWidth?=加載的圖像高度mViewHeight?
3 . 縮放因子 : 由于寬度必須填充慢組件寬度 , 這里需要縮放圖片 , 高分辨率手機(jī)需要縮小圖片 , 低分辨率手機(jī)需要放大圖片 ;
縮放因子=mViewWidth加載的圖像寬度縮放因子 = \dfrac{mViewWidth}{加載的圖像寬度 }縮放因子=加載的圖像寬度mViewWidth?
4 . 計(jì)算區(qū)域高度 : 圖像截取的寬度已知 , 組件的寬高已知 , 計(jì)算圖像截取的高度 :
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 方法中 , 獲取最新測(cè)量出來的組件寬高 , 根據(jù)以上公式 , 計(jì)算出要解碼圖像的寬高 ;
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 獲取測(cè)量的自定義 View 組件寬高mViewWidth = getMeasuredWidth();mViewHeight = getMeasuredHeight();// 根據(jù)組件的寬高 , 確定要加載的圖像的寬高if(mBitmapRegionDecoder != null){mRect.left = 0;mRect.top = 0;// 繪制的寬度就是圖像的寬度mRect.right = mImageWidth;// 根據(jù)圖像寬度 和 組件寬度 , 計(jì)算出縮放比例// 組件寬度 / 圖像寬度 = 縮放因子mScale = (float)mViewWidth / (float)mImageWidth;/*加載的圖像高度寬度 , 與組件的高度寬度比例一致mViewWidth / 加載的圖像寬度 = mViewHeight / 加載的圖像高度此處加載的圖像寬度就是實(shí)際的寬度加載的圖像高度 = mViewHeight / ( mViewWidth / 加載的圖像寬度 )mViewWidth / 加載的圖像寬度 就是縮放因子加載的圖像高度 = mViewHeight / 縮放因子*/// 根據(jù)縮放因子計(jì)算解碼高度mRect.bottom = (int) (mViewHeight / mScale);}}三、設(shè)置解碼參數(shù) 內(nèi)存復(fù)用 像素格式
設(shè)置圖像解碼參數(shù) :
① 關(guān)閉尺寸解碼 : 之前解碼圖像尺寸 , 將 BitmapFactory.Options 的 inJustDecodeBounds 屬性設(shè)置為了 true , 現(xiàn)在要開始解碼圖像數(shù)據(jù)了 , 需要關(guān)閉該選項(xiàng) , 設(shè)置為 false ;
② 設(shè)置像素格式 : 如果不需要顯示透明度 , 就設(shè)置 BitmapFactory.Options 的 inPreferredConfig 像素格式為 Bitmap.Config.RGB_565 , 該像素格式每個(gè)像素占 2 字節(jié)內(nèi)存 ;
③ 設(shè)置可變 : 這是內(nèi)存復(fù)用生效的前提 , 設(shè)置 inMutable 為 true ;
④ 設(shè)置復(fù)用內(nèi)存的 Bitmap 對(duì)象 : 每次解碼操作前都要設(shè)置一次 , 解碼時(shí)會(huì)復(fù)用該 Bitmap 中的內(nèi)存 ;
2 . 代碼示例 :
/*** Bitmap 解碼選項(xiàng)*/ private BitmapFactory.Options mOptions;// ... // 設(shè)置 Bitmap 內(nèi)存復(fù)用 mOptions.inMutable = true; // 設(shè)置可變 // 內(nèi)存復(fù)用 mOptions.inBitmap = mBitmap; mOptions.inPreferredConfig = Bitmap.Config.RGB_565; // 設(shè)置像素格式 RGB 565 mOptions.inJustDecodeBounds = false; // 讀取完畢之后, 就需要解析實(shí)際的 Bitmap 圖像數(shù)據(jù)了四、圖像繪制
1 . 圖像繪制 :
① 設(shè)置圖像區(qū)域解碼器 : 在為自定義組件設(shè)置圖片時(shí) , 設(shè)置區(qū)域解碼器 , 因?yàn)橐O(shè)置區(qū)域解碼的數(shù)據(jù)源 , 因此必須在用戶設(shè)置圖片時(shí) , 才可以創(chuàng)建區(qū)域解碼器 ;
② 設(shè)置內(nèi)存復(fù)用 : 每次解碼時(shí) , 都要設(shè)置一下內(nèi)存復(fù)用的 Bitmap 對(duì)象 ; mOptions.inBitmap = mBitmap;
③ 解碼圖片 : 調(diào)用區(qū)域解碼器的 mBitmapRegionDecoder.decodeRegion 方法 , 解碼圖片的特定區(qū)域 ;
④ 設(shè)置圖片縮放 : 使用 Matrix 進(jìn)行圖像縮放 ; 圖像與自定義組件的尺寸不同 , 因此需要將解碼區(qū)域完全填充到自定義組件中顯示 ;
⑤ 圖像繪制 : 調(diào)用 canvas.drawBitmap 繪制圖像 , 如果需要縮放 , 傳入 Matrix 參數(shù) ;
2 . 代碼示例 :
/*** 圖像區(qū)域解碼器*/private BitmapRegionDecoder mBitmapRegionDecoder;// .../*** 設(shè)置顯示的圖片* @param inputStream*/public void setImage(InputStream inputStream){// ...try {// Bitmap 區(qū)域解碼器mBitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);} catch (IOException e) {e.printStackTrace();}// 設(shè)置圖片完畢后 , 刷新自定義組件requestLayout();}// ...@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if(mBitmapRegionDecoder == null) return;// 內(nèi)存復(fù)用mOptions.inBitmap = mBitmap;// 解碼圖片mBitmap = mBitmapRegionDecoder.decodeRegion(mRect, mOptions);// 設(shè)置繪制的圖像縮放 , x 軸和 y 軸都在 Bitmap 大小的區(qū)域基礎(chǔ)上 , 縮放 mScale 倍Matrix matrix = new Matrix();matrix.setScale(mScale, mScale);canvas.drawBitmap(mBitmap, matrix, null);}五、執(zhí)行效果
豎屏效果 :
橫屏效果 :
六、源碼及資源下載
源碼及資源下載地址 :
-
① GitHub 工程地址 : Long_Graph_Loading
-
② LongImageView.java 主界面代碼地址 : LongImageView.java , 這是上述示自定義組件代碼 ;
總結(jié)
以上是生活随笔為你收集整理的【Android 内存优化】自定义组件长图组件 ( 获取图像宽高 | 计算解码区域 | 设置图像解码属性 复用 像素格式 | 图像绘制 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 内存优化】自定义组件长
- 下一篇: 【Android 内存优化】自定义组件长