我们在(100; 100)坐标中有一个红点。如果我们在拖动后单击到红点,它将保存(100; 100)坐标。但是,如果我们放大或缩小,其坐标将完全不同于(100; 100)。
缩放后如何正确计算x和y?
class CanvasView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
companion object {
private const val INVALID_POINTER_ID = -1
}
private var posX: Float = 0f
private var posY: Float = 0f
private var lastTouchX: Float = 0f
private var lastTouchY: Float = 0f
private var activePointerId = INVALID_POINTER_ID
private val scaleDetector: ScaleGestureDetector
private var scaleFactor = 1f
private var prevMotionType = MotionEvent.ACTION_DOWN
private var prevX = 0f
private var prevY = 0f
private val paint: Paint = Paint()
constructor(mContext: Context) : this(mContext, null)
init {
scaleDetector = ScaleGestureDetector(context, ScaleListener())
paint.strokeWidth = 1f
paint.color = Color.RED
}
public override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
canvas.scale(scaleFactor, scaleFactor, pivotX, pivotY)
canvas.translate(posX, posY)
canvas.drawCircle(100f, 100f, 10f, paint)
canvas.restore()
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
scaleDetector.onTouchEvent(ev)
val action = ev.action
when (action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
val x = ev.x
val y = ev.y
lastTouchX = x
lastTouchY = y
activePointerId = ev.getPointerId(0)
calculateIfClicked(ev)
}
MotionEvent.ACTION_MOVE -> {
val pointerIndex = ev.findPointerIndex(activePointerId)
val x = ev.getX(pointerIndex)
val y = ev.getY(pointerIndex)
if (!scaleDetector.isInProgress) {
val dx = x - lastTouchX
val dy = y - lastTouchY
posX += dx / scaleFactor
posY += dy / scaleFactor
invalidate()
}
lastTouchX = x
lastTouchY = y
calculateIfClicked(ev)
}
MotionEvent.ACTION_UP -> {
activePointerId = INVALID_POINTER_ID
calculateIfClicked(ev)
}
MotionEvent.ACTION_CANCEL -> {
activePointerId = INVALID_POINTER_ID
}
MotionEvent.ACTION_POINTER_UP -> {
val pointerIndex =
ev.action and MotionEvent.ACTION_POINTER_INDEX_MASK shr MotionEvent.ACTION_POINTER_INDEX_SHIFT
val pointerId = ev.getPointerId(pointerIndex)
if (pointerId == activePointerId) {
val newPointerIndex = if (pointerIndex == 0) 1 else 0
lastTouchX = ev.getX(newPointerIndex)
lastTouchY = ev.getY(newPointerIndex)
activePointerId = ev.getPointerId(newPointerIndex)
}
}
}
return true
}
private fun calculateIfClicked(ev: MotionEvent) {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
prevMotionType = MotionEvent.ACTION_DOWN
prevX = ev.x
prevY = ev.y
}
MotionEvent.ACTION_MOVE -> prevMotionType = MotionEvent.ACTION_MOVE
MotionEvent.ACTION_UP -> {
val delta = Math.max(
Math.abs(Math.abs(ev.x) - Math.abs(prevX)),
Math.abs(Math.abs(ev.y) - Math.abs(prevY))
)
if (prevMotionType == MotionEvent.ACTION_DOWN ||
(prevMotionType == MotionEvent.ACTION_MOVE && delta < 5)
) {
val x = ev.x - posX * scaleFactor
val y = ev.y - posY * scaleFactor
Log.d("abcd", "x: $x, y: $y")
}
}
}
}
private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
scaleFactor *= detector.scaleFactor
scaleFactor = Math.max(0.3f, Math.min(scaleFactor, 10.0f))
invalidate()
return true
}
}
}
缩放后,x和y坐标错误。他们保持了自己的位置,但这不是人们所期望的。
答案 0 :(得分:0)
起初,我决定使用此solution并按预期工作。但是后来我使用了pskink建议我使用的解决方案。它比我以前的选择更简单,更短,更正确。示例:
interface OnMatrixChangeListener {
fun onChange(theMatrix: Matrix)
}
class ChartView2(context: Context, attrs: AttributeSet?) : View(context, attrs), OnMatrixChangeListener {
private var theMatrix = Matrix()
private var detector = MatrixGestureDetector(theMatrix, this)
private var paint = Paint()
private var colors = intArrayOf(Color.RED, Color.GREEN, Color.BLUE)
private var colorNames = arrayOf("RED", "GREEN", "BLUE")
private var centers = arrayOf(PointF(100f, 100f), PointF(400f, 100f), PointF(250f, 360f))
var inverse = Matrix()
override fun onTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
theMatrix.invert(inverse)
val pts = floatArrayOf(event.x, event.y)
inverse.mapPoints(pts)
for (i in colors.indices) {
if (Math.hypot((pts[0] - centers[i].x).toDouble(), (pts[1] - centers[i].y).toDouble()) < 100)
Log.d("abcd", colorNames[i] + " circle clicked")
}
}
detector.onTouchEvent(event)
return true
}
override fun onDraw(canvas: Canvas) {
canvas.concat(theMatrix)
for (i in colors.indices) {
paint.color = colors[i]
canvas.drawCircle(centers[i].x, centers[i].y, 100f, paint)
}
}
override fun onChange(theMatrix: Matrix) {
invalidate()
}
}
internal class MatrixGestureDetector(private val mMatrix: Matrix, listener: OnMatrixChangeListener) {
private var ptpIdx = 0
private val mTempMatrix = Matrix()
private val mListener: OnMatrixChangeListener?
private val mSrc = FloatArray(4)
private val mDst = FloatArray(4)
private var mCount: Int = 0
init {
this.mListener = listener
}
fun onTouchEvent(event: MotionEvent) {
if (event.pointerCount > 2) {
return
}
val action = event.actionMasked
val index = event.actionIndex
when (action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
val idx = index * 2
mSrc[idx] = event.getX(index)
mSrc[idx + 1] = event.getY(index)
mCount++
ptpIdx = 0
}
MotionEvent.ACTION_MOVE -> {
for (i in 0 until mCount) {
val idx = ptpIdx + i * 2
mDst[idx] = event.getX(i)
mDst[idx + 1] = event.getY(i)
}
mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount)
mMatrix.postConcat(mTempMatrix)
mListener?.onChange(mMatrix)
System.arraycopy(mDst, 0, mSrc, 0, mDst.size)
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
if (event.getPointerId(index) == 0) ptpIdx = 2
mCount--
}
}
}
}