[TOC]
# 1. 前言
剛嘗試使用Bitmap的尺寸壓縮來進行圖片大小的設置,但通過設置BitmapFactory.Options.inSampleSize來進行二次加載并沒有達到預想的效果。在之前的[博客](https://blog.csdn.net/qq_26460841/article/details/119785601)中提到:
> BitmapFactory.Options類中提供了inSampleSize屬性,如果設置該值大于1,則請求解碼器對原始圖像進行二次采樣,返回較小的圖像以節省內存。樣本大小是任一維度中對應于解碼位圖中單個像素的像素數。例如,inSampleSize=4返回的圖像的寬度/高度為原始圖像的1/4,像素數為1/16。任何小于等于1的值都被視為1。
但是這個尺寸只是像素尺寸,也就是下面的代碼:
~~~
/**
* 圖片尺寸壓縮
* @param bitmap 圖片
* @param width 目標寬度
* @param height 目標高度
* @return 壓縮后的圖片
*/
fun compressBitmap(bitmap: Bitmap, width: Int, height: Int): Bitmap{
// 裝載Bitmap數據到字節數組
val byteArrayOutputStream = ByteArrayOutputStream()
// Write a compressed version of the bitmap to the specified outputstream.
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
val byteArray = byteArrayOutputStream.toByteArray() // byte[]
// 獲取BitmapFactory.Options
val options = BitmapFactory.Options()
BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options)
// 設置采樣率
options.inSampleSize = calculateInSampleSize(options, width, height)
// 這里需要得到bitmap的實例,故而設置為false
options.inJustDecodeBounds = false
val tempBitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size, options)
return tempBitmap
}
/**
* 計算采樣率
*/
private fun calculateInSampleSize(options: BitmapFactory.Options, width: Int, height: Int): Int{
val imgRealWidth = options.outWidth;
val imgRealHeight = options.outHeight;
var inSampleSize = 1;
if(imgRealWidth > width || imgRealHeight > height){
if(imgRealWidth > width){
inSampleSize = Math.round(imgRealWidth.toFloat() / width.toFloat()); // 四舍五入
}else{
inSampleSize = Math.round(imgRealHeight.toFloat() / height.toFloat());
}
}
return inSampleSize;
}
~~~
上面代碼的應用場景為圖片的大小超過`16MB`,導致不能顯示的問題。但實際上我這里的需求為讓圖片顯示在固定大小的容器中,且隨著容器大小的改變可以自適應。因為在ondraw中直接使用canvas來繪制bitmap圖片就默認按照圖片的原始大小來繪制的。
# 2. 分析
在ImageView中,我們常常可以指定scaleType值來讓ImageView按照默認的縮放類型進行縮放,而這恰好就是我所需要的功能。故而考慮將這個功能copy到自己的自定義View中。
## 2.1 源碼分析
因為我們設置是在xml配置文件中通過如下的配置進行的:
~~~
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop">
</ImageView>
~~~
所以,我們需要在ImageView的構造函數中,找到含有AttributeSet的構造函數,可以看到其解析:
~~~
final int index = a.getInt(R.styleable.ImageView_scaleType, -1);
if (index >= 0) {
setScaleType(sScaleTypeArray[index]);
}
~~~
對于setScaleType函數:
~~~
public void setScaleType(ScaleType scaleType) {
if (scaleType == null) {
throw new NullPointerException();
}
if (mScaleType != scaleType) {
mScaleType = scaleType;
requestLayout();
invalidate();
}
}
~~~
主要的部分也就是判斷縮放類型是否和上一個一致,如果不一致就重新賦值。也就是我們這里需要關注mScaleType。進行簡單的源碼文件內搜索,可以看到在configureBounds中對其進行了縮放類型的判斷以及處理,這里簡要摘要:
~~~
private void configureBounds() {
if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
/* If the drawable has no intrinsic size, or we're told to
scaletofit, then we just fill our entire view.
*/
mDrawable.setBounds(0, 0, vwidth, vheight);
mDrawMatrix = null;
} else {
// We need to do the scaling ourself, so have the drawable
// use its native size.
mDrawable.setBounds(0, 0, dwidth, dheight);
if (ScaleType.MATRIX == mScaleType) {
// Use the specified matrix as-is.
if (mMatrix.isIdentity()) {
mDrawMatrix = null;
} else {
mDrawMatrix = mMatrix;
}
} else if (fits) {
// The bitmap fits exactly, no transform needed.
mDrawMatrix = null;
} else if (ScaleType.CENTER == mScaleType) {
// Center bitmap in view, no scaling.
mDrawMatrix = mMatrix;
mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
Math.round((vheight - dheight) * 0.5f));
} else if (ScaleType.CENTER_CROP == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
}
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
} else if (ScaleType.CENTER_INSIDE == mScaleType) {
mDrawMatrix = mMatrix;
float scale;
float dx;
float dy;
if (dwidth <= vwidth && dheight <= vheight) {
scale = 1.0f;
} else {
scale = Math.min((float) vwidth / (float) dwidth,
(float) vheight / (float) dheight);
}
dx = Math.round((vwidth - dwidth * scale) * 0.5f);
dy = Math.round((vheight - dheight * scale) * 0.5f);
mDrawMatrix.setScale(scale, scale);
mDrawMatrix.postTranslate(dx, dy);
} else {
// Generate the required transform.
mTempSrc.set(0, 0, dwidth, dheight);
mTempDst.set(0, 0, vwidth, vheight);
mDrawMatrix = mMatrix;
mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
}
}
}
~~~
可以發現其實也就是對mDrawMatrix的一系列變換,包括平移、縮放等。然后在onDraw方法中,也會對應的進行判斷:
~~~
final int saveCount = canvas.getSaveCount();
canvas.save();
if (mCropToPadding) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
scrollX + mRight - mLeft - mPaddingRight,
scrollY + mBottom - mTop - mPaddingBottom);
}
canvas.translate(mPaddingLeft, mPaddingTop);
if (mDrawMatrix != null) {
// 對matrix的變換應用到canvas上的所有對象。
canvas.concat(mDrawMatrix);
}
mDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
~~~
也就是變換其實是Matrix來施加的,最終通過canvas.concat來應用在畫布之上。所以接下來繼續了解Matrix。
- 介紹
- UI
- MaterialButton
- MaterialButtonToggleGroup
- 字體相關設置
- Material Design
- Toolbar
- 下拉刷新
- 可折疊式標題欄
- 懸浮按鈕
- 滑動菜單DrawerLayout
- NavigationView
- 可交互提示
- CoordinatorLayout
- 卡片式布局
- 搜索框SearchView
- 自定義View
- 簡單封裝單選
- RecyclerView
- xml設置點擊樣式
- adb
- 連接真機
- 小技巧
- 通過字符串ID獲取資源
- 自定義View組件
- 使用系統控件重新組合
- 旋轉菜單
- 輪播圖
- 下拉輸入框
- 自定義VIew
- 圖片組合的開關按鈕
- 自定義ViewPager
- 聯系人快速索引案例
- 使用ListView定義側滑菜單
- 下拉粘黏效果
- 滑動沖突
- 滑動沖突之非同向沖突
- onMeasure
- 繪制字體
- 設置畫筆Paint
- 貝賽爾曲線
- Invalidate和PostInvalidate
- super.onTouchEvent(event)?
- setShadowLayer與陰影效果
- Shader
- ImageView的scaleType屬性
- 漸變
- LinearGradient
- 圖像混合模式
- PorterDuffXfermode
- 橡皮擦效果
- Matrix
- 離屏繪制
- Canvas和圖層
- Canvas簡介
- Canvas中常用操作總結
- Shape
- 圓角屬性
- Android常見動畫
- Android動畫簡介
- View動畫
- 自定義View動畫
- View動畫的特殊使用場景
- LayoutAnimation
- Activity的切換轉場效果
- 屬性動畫
- 幀動畫
- 屬性動畫監聽
- 插值器和估值器
- 工具
- dp和px的轉換
- 獲取屏幕寬高
- JNI
- javah命令
- C和Java相互調用
- WebView
- Android Studio快捷鍵
- Bitmap和Drawable圖像
- Bitmap簡要介紹
- 圖片縮放和裁剪效果
- 創建指定顏色的Bitmap圖像
- Gradle本地倉庫
- Gradle小技巧
- RxJava+Okhttp+Retrofit構建網絡模塊
- 服務器相關配置
- node環境配置
- 3D特效