概述
package com.tty.zoomview.view import android.content.Context import android.graphics.* import android.util.Log import android.view.GestureDetector import android.view.GestureDetector.SimpleOnGestureListener import android.view.MotionEvent import android.view.ScaleGestureDetector import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener import android.view.View import androidx.annotation.IntDef import com.tty.zoomview.R import java.lang.annotation.Retention import java.lang.annotation.RetentionPolicy /** * 缩放、拖拽、双击缩放 自定义view * * * 小建议: 1,尽量不要在onDraw里面重复赋值 2,你现在缩放、移动是把matrix应用到画布上,可以试试针对bitmap,用canvas.drawBitmap(bitmap,matrix,paint)这个重载来实现 3,可以加一个功能,点击屏幕,判断当前点击的是否在缩放、移动后的bitmap上 */ class ScaleZoomView(context: Context) : View(context) { var mGestureDetector: GestureDetector? = null var mScaleGestureDetector: ScaleGestureDetector? = null // 画布当前的 Matrix, 用于获取当前画布的一些状态信息,例如缩放大小,平移距离等 private var mCanvasMatrix = Matrix() // 将用户触摸的坐标转换为画布上坐标所需的 Matrix, 以便找到正确的缩放中心位置 private val mInvertMatrix = Matrix() // 所有用户触发的缩放、平移等操作都通过下面的 Matrix 直接作用于画布上, // 将系统计算的一些初始缩放平移信息与用户操作的信息进行隔离,让操作更加直观 private val mUserMatrix = Matrix() private var mBitmap: Bitmap = BitmapFactory.decodeResource(resources, R.mipmap.img) // 基础的缩放和平移信息,该信息与用户的手势操作无关 private var mBaseScale = 0f private var mBaseTranslateX = 0f private var mBaseTranslateY = 0f private var mPaint: Paint = Paint() companion object { private const val MSCALE_X = 0 private const val MSKEW_X = 1 private const val MTRANS_X = 2 private const val MSKEW_Y = 3 private const val MSCALE_Y = 4 private const val MTRANS_Y = 5 private const val MPERSP_0 = 6 private const val MPERSP_1 = 7 private const val MPERSP_2 = 8 //--- 限制缩放比例 --- private const val MAX_SCALE = 4.0f //最大缩放比例 private const val MIN_SCALE = 0.5f // 最小缩放比例 } init { mPaint.style = Paint.Style.STROKE mPaint.strokeWidth = 6f initGesture(context) } //视图大小发生改变后回调 override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) if (mBitmap.width * 1.0f / mBitmap.height > w * 1.0f / h) { mBaseScale = w * 1.0f / mBitmap.width mBaseTranslateX = 0f mBaseTranslateY = (h - mBitmap.height * mBaseScale) / 2 } else { mBaseScale = h * 1.0f / mBitmap.height * 1.0f mBaseTranslateX = (w - mBitmap.width * mBaseScale) / 2 mBaseTranslateY = 0f } } //1,尽量不要在onDraw里面重复赋值 //2,你现在缩放、移动是把matrix应用到画布上,可以试试针对bitmap,用canvas.drawBitmap(bitmap,matrix,paint)这个重载来实现 override fun onDraw(canvas: Canvas) { super.onDraw(canvas) canvas.translate(mBaseTranslateX, mBaseTranslateY)//拖拽 平移 canvas.scale(mBaseScale, mBaseScale)//缩放 canvas.save() canvas.concat(mUserMatrix)//用户操作应用到画布上 mCanvasMatrix = canvas.matrix mCanvasMatrix.invert(mInvertMatrix) 求逆,逆矩阵被赋值 canvas.drawBitmap(mBitmap, 0f, 0f, mPaint)//把bitmap显示到left, top所指定的左上角位置. 坐标(0,0) canvas.restore() //2,你现在缩放、移动是把matrix应用到画布上,可以试试针对bitmap,用canvas.drawBitmap(bitmap,matrix,paint)这个重载来实现 /* mUserMatrix.setTranslate(mBaseTranslateX, mBaseTranslateY) mUserMatrix.setScale(mBaseScale, mBaseScale) mUserMatrix.postRotate(30f) canvas.drawBitmap(mBitmap, mUserMatrix, mPaint) canvas.save() canvas.restore()*/ } //手势处理 // 3,可以加一个功能,点击屏幕,判断当前点击的是否在缩放、移动后的bitmap上 private fun initGesture(context: Context) { //拖拽 mGestureDetector = GestureDetector(context, object : SimpleOnGestureListener() { override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {//e1:首次按下的事件 e2:移动中的事件 distance:沿X轴或者Y轴 滚动的距离。这不是e1和e2之间的距离。 if (isInitRect(e1.x, e1.y)) {//触点是否在图片上 val scale = getMatrixValue(MSCALE_X, mCanvasMatrix)//MSCALE_X:X轴缩放比 得到mCanvasMatrix矩阵的X坐标值 mUserMatrix.preTranslate(-distanceX / scale, -distanceY / scale) //fixTranslate() // 在用户滚动时不进行修正,保证用户滚动时也有响应, 在用户抬起手指后进行修正 invalidate() return true } return false } //双击缩放 override fun onDoubleTap(e: MotionEvent): Boolean { if (isInitRect(e.x, e.y)) {//触点是否在图片上 if (!mUserMatrix.isIdentity) {//是否是Matrix mUserMatrix.reset() } else { val points = mapPoint(e.x, e.y, mInvertMatrix)//将坐标转换为画布坐标,返回数组 mUserMatrix.postScale(MAX_SCALE, MAX_SCALE, points[0], points[1])//X轴的缩放大小,Y轴的缩放大小,缩放中心点 } fixTranslate()//修正缩放 invalidate()//重新绘制 draw() return true } return false } }) //onScale 缩放被触发(会调用0次或者多次),如果返回 true 则表示当前缩放事件已经被处理,检测器会重新积累缩放因子,返回 false 则会继续积累缩放因子。 mScaleGestureDetector = ScaleGestureDetector(context, object : SimpleOnScaleGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { var scaleFactor = detector.scaleFactor // 缩放因子 val fx = detector.focusX // 缩放中心,x坐标 val fy = detector.focusY // 缩放中心y坐标 if (isInitRect(fx, fy)) {//触点是否在图片上 val points = mapPoint(fx, fy, mInvertMatrix)//将坐标转换为画布坐标,返回数组 scaleFactor = getRealScaleFactor(scaleFactor)//得到最终的缩放比例 mUserMatrix.preScale(scaleFactor, scaleFactor, points[0], points[1])//X轴的缩放大小,Y轴的缩放大小,缩放中心点 fixTranslate()//修正缩放 invalidate()//重新绘制 draw() return true } return false } }) } // 修正缩放 回到中心点 private fun fixTranslate() { // 对 Matrix 进行预计算,并根据计算结果进行修正 val viewMatrix = matrix // 获取当前控件的Matrix viewMatrix.preTranslate(mBaseTranslateX, mBaseTranslateY) viewMatrix.preScale(mBaseScale, mBaseScale) viewMatrix.preConcat(mUserMatrix)//相当于前乘,矩阵前乘; val invert = Matrix() viewMatrix.invert(invert)//得出逆矩阵 val rect = Rect() getGlobalVisibleRect(rect) val userScale = getMatrixValue(MSCALE_X, mUserMatrix) val scale = getMatrixValue(MSCALE_X, viewMatrix) val center = mapPoint(mBitmap.width / 2.0f, mBitmap.height / 2.0f, viewMatrix) val distanceX = center[0] - width / 2.0f val distanceY = center[1] - height / 2.0f val wh = mapVectors(mBitmap.width.toFloat(), mBitmap.height.toFloat(), viewMatrix) if (userScale <= 1.0f) { mUserMatrix.preTranslate(-distanceX / scale, -distanceY / scale) } else { val lefttop = mapPoint(0f, 0f, viewMatrix) val rightbottom = mapPoint(mBitmap.width.toFloat(), mBitmap.height.toFloat(), viewMatrix) // 如果宽度小于总宽度,则水平居中 if (wh[0] < width) { mUserMatrix.preTranslate(distanceX / scale, 0f) } else { if (lefttop[0] > 0) { mUserMatrix.preTranslate(-lefttop[0] / scale, 0f) } else if (rightbottom[0] < width) { mUserMatrix.preTranslate((width - rightbottom[0]) / scale, 0f) } } // 如果高度小于总高度,则垂直居中 if (wh[1] < height) { mUserMatrix.preTranslate(0f, -distanceY / scale) } else { if (lefttop[1] > 0) { mUserMatrix.preTranslate(0f, -lefttop[1] / scale) } else if (rightbottom[1] < height) { mUserMatrix.preTranslate(0f, (height - rightbottom[1]) / scale) } } } invalidate() } override fun onTouchEvent(event: MotionEvent): Boolean { mGestureDetector?.onTouchEvent(event) mScaleGestureDetector?.onTouchEvent(event) if (event.actionMasked == MotionEvent.ACTION_UP) { fixTranslate() } return true } /** * 3,可以加一个功能,点击屏幕,判断当前点击的是否在缩放、移动后的bitmap上 */ private fun isInitRect(touchX: Float, touchY: Float): Boolean { //bitmap 初始矩形坐标 val rect = RectF(0f, 0f, mBitmap.width.toFloat(), mBitmap.height.toFloat()) val result = mCanvasMatrix.mapRect(rect)//得到转换后的矩形 Log.d("yhf", "mapRadius: $rect") Log.d("yhf", "isRect: $result") Log.d("yhf", "touchX: ====" + touchX) Log.d("yhf", "touchY:====" + touchY) Log.d("yhf", "contains)====" + rect.contains(touchX, touchY)) return rect.contains(touchX, touchY)//返回 触点是否在转换后的矩形里头 } //--- Tools //--- 将坐标转换为画布坐标 --- private fun mapPoint(x: Float, y: Float, matrix: Matrix): FloatArray { val temp = FloatArray(2) temp[0] = x temp[1] = y matrix.mapPoints(temp) return temp } private fun mapVectors(x: Float, y: Float, matrix: Matrix): FloatArray { val temp = FloatArray(2) temp[0] = x temp[1] = y matrix.mapVectors(temp) return temp } //--- 获取 Matrix 中的属性 --- private val matrixValues = FloatArray(9) @IntDef(MSCALE_X, MSKEW_X, MTRANS_X, MSKEW_Y, MSCALE_Y, MTRANS_Y, MPERSP_0, MPERSP_1, MPERSP_2) @Retention(RetentionPolicy.SOURCE) private annotation class MatrixName //取出值 取出矩阵的X、Y坐标值 private fun getMatrixValue(@MatrixName name: Int, matrix: Matrix): Float { matrix.getValues(matrixValues) return matrixValues[name] } //得到最终的缩放比例 private fun getRealScaleFactor(currentScaleFactor: Float): Float { var realScale = 1.0f val userScale = getMatrixValue(MSCALE_X, mUserMatrix) // 用户当前的缩放比例 val theoryScale = userScale * currentScaleFactor // 理论缩放数值 // 如果用户在执行放大操作并且理论缩放数据大于4.0 realScale = if (currentScaleFactor > 1.0f && theoryScale > MAX_SCALE) { MAX_SCALE / userScale } else if (currentScaleFactor < 1.0f && theoryScale < MIN_SCALE) { MIN_SCALE / userScale } else { currentScaleFactor } return realScale } }
最后
以上就是强健溪流为你收集整理的Matrix 结合手势 实现图片缩放和拖动的全部内容,希望文章能够帮你解决Matrix 结合手势 实现图片缩放和拖动所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复