彻底搞懂ImageView的ScaleType,用Matrix实现各种ScaleType效果

2,298 阅读3分钟

前言

本文主要讲解以Matrix方式实现ImageView对应的各种ScaleType效果。通过代码实现,我们能更清晰的看到各种ScaleType的区别,更深刻的理解各种ScaleType的作用及其适用场景。

先放上效果图:

123
device-2021-05-13-152148.png

正文

ScaleType是用来控制图像如何调整大小或移动以匹配ImageViewI的大小,从ImageView源码中可以得知ImageView默认的ScaleTypeFIT_CENTER

//ImageView.java
private static final ScaleType[] sScaleTypeArray = {
    ScaleType.MATRIX,
    ScaleType.FIT_XY,
    ScaleType.FIT_START,
    ScaleType.FIT_CENTER,
    ScaleType.FIT_END,
    ScaleType.CENTER,
    ScaleType.CENTER_CROP,
    ScaleType.CENTER_INSIDE
};

private void initImageView() {
   mMatrix = new Matrix();
   mScaleType = ScaleType.FIT_CENTER;

  if (!sCompatDone) {
       final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
       sCompatAdjustViewBounds = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
       sCompatUseCorrectStreamDensity = targetSdkVersion > Build.VERSION_CODES.M;
      sCompatDrawableVisibilityDispatch = targetSdkVersion < Build.VERSION_CODES.N;
       sCompatDone = true;
  }
}

那么问题来了,每一种ScaleType对应的实现是什么呢?通过查看ImageView源码,发现其内部主要是通过Matrix(3*3的矩阵)实现的。

ScaleType.FIT_XY

Scale in X and Y independently, so that src matches dst exactly. This may change the aspect ratio of the src.

  • 对X和Y方向独立缩放,直到图片铺满ImageView。这种方式可能会改变图片原本的宽高比,导致图片拉伸变形。
fun ImageView.fitXY(){
    if(drawable==null){
        return
    }
    val dwidth=drawable.intrinsicWidth
    val dheight=drawable.intrinsicHeight

    val widthPercentage = width.toFloat() / dwidth.toFloat()
    val heightPercentage = height.toFloat() / dheight.toFloat()
    val matrix = Matrix()
    matrix.setScale(widthPercentage, heightPercentage)

    scaleType=ImageView.ScaleType.MATRIX
    imageMatrix=matrix
}

ScaleType.FIT_START

Compute a scale that will maintain the original src aspect ratio, but will also ensure that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. START aligns the result to the left and top edges of dst.

  • 保持图片的宽高比,对图片进行X和Y方向缩放,直到一个方向铺满ImageView
  • 缩放后的图片与ImageView左上角对齐进行显示。
fun ImageView.fitStart(){
    if(drawable==null){
        return
    }
    val dwidth=drawable.intrinsicWidth
    val dheight=drawable.intrinsicHeight

    val widthPercentage = width.toFloat() / dwidth.toFloat()
    val heightPercentage = height.toFloat() / dheight.toFloat()
    val minPercentage = Math.min(widthPercentage, heightPercentage)

    val matrix = Matrix()
    matrix.setScale(minPercentage, minPercentage)

    scaleType=ImageView.ScaleType.MATRIX
    imageMatrix=matrix
}

ScaleType.FIT_CENTER

Compute a scale that will maintain the original src aspect ratio, but will also ensure that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. The result is centered inside dst.

  • 保持图片的宽高比,对图片进行X和Y方向缩放,直到一个方向铺满ImageView
  • 缩放后的图片居中显示在ImageView中。
fun ImageView.fitCenter(){
    if(drawable==null){
        return
    }
    val dwidth=drawable.intrinsicWidth
    val dheight=drawable.intrinsicHeight

    val widthPercentage = width.toFloat() / dwidth.toFloat()
    val heightPercentage = height.toFloat() / dheight.toFloat()
    val minPercentage = Math.min(widthPercentage, heightPercentage)

    val targetWidth = (minPercentage * dwidth).roundToInt()
    val targetHeight = (minPercentage * dheight).roundToInt()

    val matrix = Matrix()
    matrix.setScale(minPercentage, minPercentage)
    matrix.postTranslate((width-targetWidth)*0.5f, (height-targetHeight)*0.5f)

    scaleType=ImageView.ScaleType.MATRIX
    imageMatrix=matrix

}

ScaleType.FIT_END

Compute a scale that will maintain the original src aspect ratio, but will also ensure that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. END aligns the result to the right and bottom edges of dst.

  • 保持图片的宽高比,对图片进行X和Y方向缩放,直到一个方向铺满ImageView
  • 缩放后的图片与ImageView右下角对齐进行显示。
fun ImageView.fitEnd(){
    if(drawable==null){
        return
    }
    val dwidth=drawable.intrinsicWidth
    val dheight=drawable.intrinsicHeight

    val widthPercentage = width.toFloat() / dwidth.toFloat()
    val heightPercentage = height.toFloat() / dheight.toFloat()
    val minPercentage = Math.min(widthPercentage, heightPercentage)

    val matrix = Matrix()
    val targetWidth = (minPercentage * dwidth).roundToInt()
    val targetHeight = (minPercentage * dheight).roundToInt()

    matrix.setScale(minPercentage, minPercentage)
    matrix.postTranslate((width-targetWidth).toFloat(), (height-targetHeight).toFloat())

    scaleType=ImageView.ScaleType.MATRIX
    imageMatrix=matrix

}

ScaleType.CENTER

Center the image in the view, but perform no scaling.

  • 图片居中显示在ImageView中,不对图片进行缩放。
fun ImageView.center(){
    if(drawable==null){
        return
    }
    
    val dwidth=drawable.intrinsicWidth
    val dheight=drawable.intrinsicHeight

    val matrix = Matrix()
    matrix.setTranslate((width - dwidth) * 0.5f, (height - dheight) * 0.5f)

    scaleType=ImageView.ScaleType.MATRIX
    imageMatrix=matrix
}

ScaleType.CENTER_CROP

Scale the image uniformly (maintain the image's aspect ratio) so both dimensions (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding). The image is then centered in the view.

  • 保持图片的宽高比,等比例对图片进行X和Y方向缩放,直到每个方向都大于等于ImageView对应的尺寸。
  • 缩放后的图片居中显示在ImageView中,超出部分做裁剪处理。
fun ImageView.centerCrop(){

    if(drawable==null){
        return
    }

    val dwidth=drawable.intrinsicWidth
    val dheight=drawable.intrinsicHeight

    val scale: Float
    val dx:Float
    val dy:Float

    if (dwidth * height > width * dheight) {
        scale = height.toFloat() / dheight.toFloat()
        dx = (width - dwidth * scale) * 0.5f
        dy=0f
    } else {
        scale = width.toFloat() / dwidth.toFloat()
        dx=0f
        dy = (height - dheight * scale) * 0.5f
    }
    
    val matrix = Matrix()
    matrix.setScale(scale, scale)
    matrix.postTranslate(dx + 0.5f, dy+0.5f)

    scaleType=ImageView.ScaleType.MATRIX
    imageMatrix=matrix
}

ScaleType.CENTER_INSIDE

Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding). The image is then centered in the view.

  • 如果图片宽度<=ImageView宽度&&图片高度<=ImageView高度,不执行缩放,居中显示在ImageView中。
  • 其余情况按ScaleType.FIT_CENTER处理。
fun ImageView.centerInside(){
    if(drawable==null){
        return
    }
    val dwidth=drawable.intrinsicWidth
    val dheight=drawable.intrinsicHeight

    if(dwidth<=width && dheight<=height){
        val matrix = Matrix()
        matrix.setTranslate((width-dwidth)*0.5f, (height-dwidth)*0.5f)
        scaleType=ImageView.ScaleType.MATRIX
        imageMatrix=matrix
    }
    else{
        fitCenter()
    }

}

ScaleType.MATRIX

Scale using the image matrix when drawing

  • 使用Matrix绘制图片。

GitHub

本文相关代码已上传GitHub,地址如下: github.com/kongpf8848/…