[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特效