双击缩放使图像向图像中心缩放

Double tap to Zoom makes the Image Zoom towards the center of the Image

我目前已经实现了一个自定义 class,它实现了 AppCompatImageView、OnGestureListener 和 OnDoubleTapListener,以构建我自己的 ImageView,它具有双指缩放、双指缩放 in/out 在这篇文章的帮助下 https://daveson.medium.com/android-imageview-double-tap-and-pinch-zoom-with-multi-touch-gestures-in-kotlin-1559a5dd4a69

我在这里遗漏的是,当用户双击图像的一角时,默认情况下图像会向图像中心缩放。如何确保双击考虑到点击坐标以向该坐标缩放。

这是我的 TouchImageView class

class TouchImageView : androidx.appcompat.widget.AppCompatImageView, GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {

var matrix1: Matrix? = null
var mode = NONE

// Remember some things for zooming
var last = PointF()
var start = PointF()
var minScale = 1f
var maxScale = 3f
var m: FloatArray? = null
var viewWidth = 0
var viewHeight = 0
var saveScale = 1f
protected var origWidth = 0f
protected var origHeight = 0f
var oldMeasuredWidth = 0
var oldMeasuredHeight = 0
var mScaleDetector: ScaleGestureDetector? = null
//    var context: Context? = null
var context1 : Context? = null

constructor(context: Context) : super(context) {
    sharedConstructing(context)
}

constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
    sharedConstructing(context)
}

var mGestureDetector: GestureDetector? = null
private fun sharedConstructing(context: Context) {
    super.setClickable(true)
    this.context1 = context
    mGestureDetector = GestureDetector(context, this)
    mGestureDetector!!.setOnDoubleTapListener(this)
    mScaleDetector = ScaleGestureDetector(context, ScaleListener())
    matrix1 = Matrix()
    m = FloatArray(9)
    imageMatrix = matrix1
    scaleType = ScaleType.MATRIX
    setOnTouchListener { v, event ->
        mScaleDetector!!.onTouchEvent(event)
        mGestureDetector!!.onTouchEvent(event)
        val curr = PointF(event.x, event.y)
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                last.set(curr)
                start.set(last)
                mode = DRAG
            }
            MotionEvent.ACTION_MOVE -> if (mode == DRAG) {
                val deltaX = curr.x - last.x
                val deltaY = curr.y - last.y
                val fixTransX = getFixDragTrans(
                    deltaX, viewWidth.toFloat(),
                    origWidth * saveScale
                )
                val fixTransY = getFixDragTrans(
                    deltaY, viewHeight.toFloat(),
                    origHeight * saveScale
                )
                matrix1!!.postTranslate(fixTransX, fixTransY)
                fixTrans()
                last[curr.x] = curr.y
            }
            MotionEvent.ACTION_UP -> {
                mode = NONE
                val xDiff = Math.abs(curr.x - start.x).toInt()
                val yDiff = Math.abs(curr.y - start.y).toInt()
                if (xDiff < CLICK && yDiff < CLICK) performClick()
            }
            MotionEvent.ACTION_POINTER_UP -> mode = NONE
        }
        imageMatrix = matrix1
        invalidate()
        true // indicate event was handled
    }
}

fun setMaxZoom(x: Float) {
    maxScale = x
}

override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
    return false
}

override fun onDoubleTap(e: MotionEvent): Boolean {
    // Double tap is detected
    Log.i("MAIN_TAG", "Double tap detected")
    val origScale = saveScale
    val mScaleFactor: Float
    if (saveScale == maxScale) {
        saveScale = minScale
        mScaleFactor = minScale / origScale
    } else {
        saveScale = maxScale
        mScaleFactor = maxScale / origScale
    }
    matrix1!!.postScale(
        mScaleFactor, mScaleFactor, (viewWidth / 2).toFloat(), (
                viewHeight / 2).toFloat()
    )
    fixTrans()
    return false
}

override fun onDoubleTapEvent(e: MotionEvent): Boolean {
    return false
}

override fun onDown(e: MotionEvent): Boolean {
    return false
}

override fun onShowPress(e: MotionEvent) {}
override fun onSingleTapUp(e: MotionEvent): Boolean {
    return false
}

override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
    return false
}

override fun onLongPress(e: MotionEvent) {}
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
    return false
}

private inner class ScaleListener : SimpleOnScaleGestureListener() {
    override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
        mode = ZOOM
        return true
    }

    override fun onScale(detector: ScaleGestureDetector): Boolean {
        var mScaleFactor = detector.scaleFactor
        val origScale = saveScale
        saveScale *= mScaleFactor
        if (saveScale > maxScale) {
            saveScale = maxScale
            mScaleFactor = maxScale / origScale
        } else if (saveScale < minScale) {
            saveScale = minScale
            mScaleFactor = minScale / origScale
        }
        if (origWidth * saveScale <= viewWidth
            || origHeight * saveScale <= viewHeight
        ) matrix1!!.postScale(
            mScaleFactor, mScaleFactor, (viewWidth / 2).toFloat(), (
                    viewHeight / 2).toFloat()
        ) else matrix1!!.postScale(
            mScaleFactor, mScaleFactor,
            detector.focusX, detector.focusY
        )
        fixTrans()
        return true
    }
}

fun fixTrans() {
    matrix1!!.getValues(m)
    val transX = m!![Matrix.MTRANS_X]
    val transY = m!![Matrix.MTRANS_Y]
    val fixTransX = getFixTrans(transX, viewWidth.toFloat(), origWidth * saveScale)
    val fixTransY = getFixTrans(
        transY, viewHeight.toFloat(), origHeight
                * saveScale
    )
    if (fixTransX != 0f || fixTransY != 0f) matrix1!!.postTranslate(fixTransX, fixTransY)
}

fun getFixTrans(trans: Float, viewSize: Float, contentSize: Float): Float {
    val minTrans: Float
    val maxTrans: Float
    if (contentSize <= viewSize) {
        minTrans = 0f
        maxTrans = viewSize - contentSize
    } else {
        minTrans = viewSize - contentSize
        maxTrans = 0f
    }
    if (trans < minTrans) return -trans + minTrans
    return if (trans > maxTrans) -trans + maxTrans else 0f
}

private fun getFixDragTrans(delta: Float, viewSize: Float, contentSize: Float): Float {
    return if (contentSize <= viewSize) {
        0f
    } else delta
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    viewWidth = MeasureSpec.getSize(widthMeasureSpec)
    viewHeight = MeasureSpec.getSize(heightMeasureSpec)

    //
    // Rescales image on rotation
    //
    if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight || viewWidth == 0 || viewHeight == 0) return
    oldMeasuredHeight = viewHeight
    oldMeasuredWidth = viewWidth
    if (saveScale == 1f) {
        // Fit to screen.
        val scale: Float
        val drawable = drawable
        if (drawable == null || drawable.intrinsicWidth == 0 || drawable.intrinsicHeight == 0) return
        val bmWidth = drawable.intrinsicWidth
        val bmHeight = drawable.intrinsicHeight
        Log.d("bmSize", "bmWidth: $bmWidth bmHeight : $bmHeight")
        val scaleX = viewWidth.toFloat() / bmWidth.toFloat()
        val scaleY = viewHeight.toFloat() / bmHeight.toFloat()
        scale = Math.min(scaleX, scaleY)
        matrix1!!.setScale(scale, scale)

        // Center the image
        var redundantYSpace = (viewHeight.toFloat()
                - scale * bmHeight.toFloat())
        var redundantXSpace = (viewWidth.toFloat()
                - scale * bmWidth.toFloat())
        redundantYSpace /= 2.toFloat()
        redundantXSpace /= 2.toFloat()
        matrix1!!.postTranslate(redundantXSpace, redundantYSpace)
        origWidth = viewWidth - 2 * redundantXSpace
        origHeight = viewHeight - 2 * redundantYSpace
        imageMatrix = matrix1
    }
    fixTrans()
}

companion object {
    // We can be in one of these 3 states
    const val NONE = 0
    const val DRAG = 1
    const val ZOOM = 2
    const val CLICK = 3
}

}

你试过了吗https://github.com/MikeOrtiz/TouchImageView 我可以想象它具有您需要的所有功能并且维护得很好