我创建了自定义 View ,您可以在其中触摸和缩放它。
大部分工作都是在这个 post 的帮助下创建的
接下来我观察到,如果我想放大图像,它总是放大到左上角。
这是我的 onDraw()
方法:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor);
// actual drawing
canvas.getClipBounds(mRect);
canvas.restore();
}
所以这是
canvas.scale(px, py);
的正常行为,我希望从我的角度来看缩放到中心点(稍后我将采用 focalX
、 focalY
坐标,但现在只是居中)。我决定更换canvas.scale(mScaleFactor, mScaleFactor);
到canvas.scale(mScaleFactor, mScaleFactor, getWidth()/2-mPosX, getHeight()/2-mPosY);
现在它在图像的中间完美地缩放。但它以某种方式影响了我的 mPosX、mPosY 坐标。我附上一张图片以便更好地理解。
左上角是等效的 mPosX = 0,mPosY=0,没关系。
接下来,如果我要缩小,接下来会发生:
现在它决定左上角等效于
mPosX=0,mPosY=0
(左上角)。但在我的逻辑中,它应该像 (mPosX=100, mPosY=130) 或类似的东西。所以我的问题是我正在从我的实际内容 View 中滑出。我应该怎么做才能防止这种行为?
最佳答案
我对接触点计算和引用 this answer 有同样的问题,我使用指标以自己的方式解决了这个问题。以上答案提供了更好的捏合缩放和拖动功能,但没有提供正确的接触点。这是完整的代码,忽略我自己的变量。
package axis.nbapp;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import java.io.IOException;
public class NewView extends ViewGroup {
float dx;
float dy;
float scaleFac;
Region r;
private Path mPath;
private Paint mPaint;
private Path mTransformedPath;
Stencil stencil;
private Paint cColor;
boolean scaling;
Canvas bmcanvas;
SVG svg = null;
int viewWidth;
int viewHeight;
float svgWidth;
float svgHeight;
// States.
private static final byte NONE = 0;
private static final byte DRAG = 1;
private static final byte ZOOM = 2;
private byte mode = NONE;
// Matrices used to move and zoom image.
private Matrix matrix = new Matrix();
private Matrix matrixInverse = new Matrix();
private Matrix savedMatrix = new Matrix();
// Parameters for zooming.
private PointF start = new PointF();
private PointF mid = new PointF();
private float oldDist = 1f;
private float[] lastEvent = null;
private long lastDownTime = 0l;
private float[] mDispatchTouchEventWorkingArray = new float[2];
private float[] mOnTouchEventWorkingArray = new float[2];
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mDispatchTouchEventWorkingArray[0] = ev.getX();
mDispatchTouchEventWorkingArray[1] = ev.getY();
mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
ev.setLocation(mDispatchTouchEventWorkingArray[0], mDispatchTouchEventWorkingArray[1]);
return super.dispatchTouchEvent(ev);
}
public NewView(Context context) {
super(context);
init(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
public NewView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public NewView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
}
/**
* Determine the space between the first two fingers
*/
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
/**
* Calculate the mid point of the first two fingers
*/
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
private float[] scaledPointsToScreenPoints(float[] a) {
matrix.mapPoints(a);
return a;
}
private float[] screenPointsToScaledPoints(float[] a) {
matrixInverse.mapPoints(a);
return a;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
float[] values = new float[9];
matrix.getValues(values);
canvas.save();
canvas.translate(values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]);
canvas.scale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
super.dispatchDraw(canvas);
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// handle touch events here
mOnTouchEventWorkingArray[0] = event.getX();
mOnTouchEventWorkingArray[1] = event.getY();
mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);
event.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
mode = DRAG;
lastEvent = null;
long downTime = event.getDownTime();
if (downTime - lastDownTime < 300l) {
float density = getResources().getDisplayMetrics().density;
if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) < 40.f * density) {
savedMatrix.set(matrix);
mid.set(event.getX(), event.getY());
mode = ZOOM;
lastEvent = new float[4];
lastEvent[0] = lastEvent[1] = event.getX();
lastEvent[2] = lastEvent[3] = event.getY();
}
lastDownTime = 0l;
} else {
lastDownTime = downTime;
}
start.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
lastEvent = new float[4];
lastEvent[0] = event.getX(0);
lastEvent[1] = event.getX(1);
lastEvent[2] = event.getY(0);
lastEvent[3] = event.getY(1);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
lastEvent = null;
break;
case MotionEvent.ACTION_MOVE:
final float density = getResources().getDisplayMetrics().density;
if (mode == DRAG) {
matrix.set(savedMatrix);
dx = event.getX() - start.x;
dy = event.getY() - start.y;
matrix.postTranslate(dx, dy);
matrix.invert(matrixInverse);
if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) > 20.f * density) {
lastDownTime = 0l;
}
} else if (mode == ZOOM) {
if (event.getPointerCount() > 1) {
float newDist = spacing(event);
if (newDist > 10f * density) {
matrix.set(savedMatrix);
float scale = (newDist / oldDist);
scaleFac = newDist;
matrix.postScale(scale, scale, mid.x, mid.y);
matrix.invert(matrixInverse);
}
} else {
matrix.set(savedMatrix);
float scale = event.getY() / start.y;
scaleFac = scale;
matrix.postScale(scale, scale, mid.x, mid.y);
matrix.invert(matrixInverse);
}
}
break;
}
float[] values = new float[9];
matrix.getValues(values);
//int x = ((int)(event.getX() - values[2]*values[Matrix.MSCALE_X]))/(int)values[0];
//int y = ((int)(event.getY() - values[5]*values[Matrix.MSCALE_Y]))/(int)values[4];
int x = ((int)(event.getX() / values[Matrix.MSCALE_X] - (values[Matrix.MTRANS_X]/values[Matrix.MSCALE_X])));
int y = ((int)(event.getY() / values[Matrix.MSCALE_Y] - (values[Matrix.MTRANS_Y]/values[Matrix.MSCALE_Y])));
invalidate();
return true;
}
}
检查
onTouchEvent
您可以从以下计算中找到转换后的 x、y 接触点。 int x = ((int)(event.getX() / values[Matrix.MSCALE_X] - (values[Matrix.MTRANS_X]/values[Matrix.MSCALE_X])));
int y = ((int)(event.getY() / values[Matrix.MSCALE_Y] - (values[Matrix.MTRANS_Y]/values[Matrix.MSCALE_Y])));