背景
ImageView的scaleType默认显示图片是这样,但是有时候设计稿需求希望图片左右能紧贴着ImageView左右边缘,又不破坏图片的比例,用自带的matrix,centerCrop等都可以满足
但是都会造成图片的某些区域被裁剪了,如果设计稿只能接受底部被裁剪,其他边区域正常显示,那系统自带的scaleType则无法满足需求。这时需要自定义新的scaleType来满足设计要求
源码解析
- 以宽度为基准,计算图片与ImageView的缩放比例
scale = viewWidth.toFloat() / drawableWidth.toFloat()
- 根据自定义的scale_type,算出图片显示区域,viewHeight / scale为图片显示高度
var drawableRect: RectF? = null
if (mCropType == FROM_TOP) {
drawableRect = RectF(0f, 0f, drawableWidth.toFloat(), viewHeight / scale)
} else if (mCropType == FROM_BOTTOM) {
drawableRect = RectF(
0f,
drawableHeight - viewHeight / scale,
drawableWidth.toFloat(),
drawableHeight.toFloat()
)
}
- 使用setImageMatrix设置图片绘制边界
val viewRect = RectF(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat())
matrix.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.FILL)
setImageMatrix(matrix)
完整源码
class MatrixImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
companion object {
const val DEFAULT = 0
const val FROM_TOP = 1
const val FROM_BOTTOM = 2
}
private var mCropType = DEFAULT
init {
val ta = context.obtainStyledAttributes(attrs, R.styleable.MatrixImageView, 0, 0)
mCropType = ta.getInt(R.styleable.MatrixImageView_scale_type, DEFAULT)
if (mCropType != DEFAULT) {
setScaleType(ScaleType.MATRIX)
}
ta.recycle()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (mCropType != DEFAULT) {
setupImageMatrixRect()
}
}
private fun setupImageMatrixRect() {
if (getDrawable() == null) {
return
}
val matrix = getImageMatrix()
val scale: Float
val viewWidth = width - paddingLeft - paddingRight
val viewHeight = height - paddingLeft - paddingRight
val drawableWidth = getDrawable().intrinsicWidth
val drawableHeight = getDrawable().intrinsicHeight
scale = viewWidth.toFloat() / drawableWidth.toFloat()
var drawableRect: RectF? = null
if (mCropType == FROM_TOP) {
drawableRect = RectF(0f, 0f, drawableWidth.toFloat(), viewHeight / scale)
} else if (mCropType == FROM_BOTTOM) {
drawableRect = RectF(
0f,
drawableHeight - viewHeight / scale,
drawableWidth.toFloat(),
drawableHeight.toFloat()
)
}
val viewRect = RectF(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat())
matrix.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.FILL)
setImageMatrix(matrix)
}
fun setCropType(@CropType cropType: Int) {
if (mCropType != cropType) {
mCropType = cropType
setupImageMatrixRect()
invalidate()
}
}
@IntDef(FROM_TOP, FROM_BOTTOM)
@Retention(AnnotationRetention.SOURCE)
annotation class CropType
}
<declare-styleable name="MatrixImageView">
<attr name="scale_type">
<enum name="matrix_top" value="1" />
<enum name="matrix_bottom" value="2" />
</attr>
</declare-styleable>