我是靠谱客的博主 强健溪流,最近开发中收集的这篇文章主要介绍Matrix 结合手势 实现图片缩放和拖动,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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 结合手势 实现图片缩放和拖动所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部