In an app made by myself, I met a requirement that pictures should support content zooming and content moving preview, so I chose PhotoView to realize this function. However, PhotoView has a disadvantage of content clipping using centorCrop, so I tried to use custom ImageView to realize this function, and the code is directly shown below

* Author: Sean-Shen * Date: 2021/1/19 * Desc: */ import android.content.Context import android.graphics.Matrix import android.graphics.RectF import android.util.AttributeSet import android.view.MotionEvent import android.view.ScaleGestureDetector import androidx.appcompat.widget.AppCompatImageView import com.blankj.utilcode.util.LogUtils import kotlin.math.abs class MyImageView : AppCompatImageView { private var oldX: Float = 0f private var oldY: Float = 0f private var mMatrix: Matrix? = null private var initScale: Float = 1.0f private var totalScale: Float = 1.0f // Zoom value when zooming in // Zoom priority, which is higher than drag priority Private var mScaleGestureDetector: ScaleGestureDetector? = null constructor(context: Context) : super(context) { initImageView() } constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) { initImageView() } constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super( context, attributeSet, DefStyleAttr) {initImageView()} /** * Initialize some configurations */ Private Fun initImageView() {mScaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.OnScaleGestureListener { override fun onScale(detector: ScaleGestureDetector?) : Boolean {// To perform boundary checking on detector? .scaleFactor? .let {totalScale += it-initScale logutils. e("Sean-- >totalScale$totalScale") scaleImg(it)} return true Override Fun onScaleBegin(Detector: ScaleGestureDetector?) override Fun onScaleBegin(Detector: ScaleGestureDetector? : Boolean { return true } override fun onScaleEnd(detector: ScaleGestureDetector?) { } }) } override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { super.onLayout(changed, left, top, right, bottom) val tempRectF = getMatrixRectF() LogUtils.e("Sean--->${tempRectF.right} + ${tempRectF.left} + ${tempRectF.bottom} + ${tempRectF.top} + width:$width + height$height") centerLayout() } override fun onTouchEvent(event: MotionEvent?) : Boolean { mScaleGestureDetector? .onTouchEvent(event) event? .let { when (it.action) { MotionEvent.ACTION_DOWN -> { oldX = it.x oldY = it.y } MotionEvent.ACTION_MOVE -> { if (abs((it.x - oldX).toInt()) > 2 || abs((it.y - oldY).toInt()) > 2) { translateBitmap(it.x - oldX, Y cancelLongPress(); // cancelLongPress() ACTION_UP -> {// Handle the bounceback handleBorder()} else -> return@let}} return super.onTouchEvent(event)} Private fun scaleImg(scaleFactor: Float) { ScaleBitmap (scaleFactor, scaleFactor)} else if (totalScale <= 1.0f) { HandleScaleBorder ()}} private fun scaleBitmap(xScale: Float, yScale: Float) {mMatrix? .postscale (xScale, yScale) imageMatrix = mMatrix} /** ** private fun translateBitmap(Float, Float) Float) { mMatrix? .postTranslate(offsetX, Private fun handleBorder() {var tmpOffsetX = 0f var tmpOffsetY = 0f Val tempRectF = getMatrixRectF() if (temprectf.left > 0) { TmpOffsetX = -temprectf.left} if (temprectf.right < this.width) {// Right to left, TmpOffsetX = this.width -temprectf.right} if (temprectf.top > 0) {// Top side down, TmpOffsetY = -temprectf.top} if (temprectf.bottom < this.height) {// There is a white edge at the bottom tmpOffsetY = this.height-temprectf. bottom} translateBitmap(tmpOffsetX, */ private fun handleScaleBorder() {val tempRectF = getMatrixRectF() if (width > height) && tempRectF.left <= 0) { val tempScaleFactor = width.toFloat() / (tempRectF.right - tempRectF.left) scaleBitmap(tempScaleFactor, tempScaleFactor) } else if (height >= width && tempRectF.top <= 0) { val tempScaleFactor = height.toFloat() / (tempRectF.bottom - tempRectF.top) scaleBitmap(tempScaleFactor, Private fun getMatrixRectF(): getMatrixRectF(); RectF { val rectF = RectF() val mDrawable = drawable mDrawable? .let { rectF.set(0f, 0f, it.intrinsicWidth.toFloat(), It. IntrinsicHeight. ToFloat mMatrix? ()). MapRect (rectF)} return rectF} / * * * middle area is shown in the picture the View in the * / private fun centerLayout() { val tempRectF = getMatrixRectF() val offsetX = -(tempRectF.right - tempRectF.left - width) / 2 val offsetY = -(tempRectF.bottom - tempRectF.top - height) / 2 val matrix = Matrix() matrix.postTranslate(offsetX, ImageMatrix = matrix} // private fun getScale(): Float { val matrixValues = FloatArray(9) matrix.getValues(matrixValues) return matrixValues[Matrix.MSCALE_X] } }Copy the code