复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255package 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内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复