提问人:Falling Into Infinity 提问时间:8/26/2016 最后编辑:CommunityFalling Into Infinity 更新时间:9/10/2016 访问量:2251
在 Android 画布上应用缩放效果后,缩放位置错误
Wrong scale position after applying zoom effect on Android canvas
问:
首先,这是最初在这里提出的后续问题,在 Android 中平移、缩放和缩放 Canvas 绘图的自定义视图
由于还没有答案,我终于使用 android-gesture-detectors 解决了我的问题
应用缩放/缩放手势后,我发现画布绘图坐标仍然指向旧位置(在应用缩放之前),并且没有在完全相同的触摸坐标上绘图。基本上,缩放或拖动画布后,我无法获得正确的画布坐标。
在缩放之前,
缩小后,触摸点将绘制在上一个位置上。我希望它绘制在当前的触摸位置,
示例代码,
public class DrawingView extends View {
private void setupDrawing() {
mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
mgd = new MoveGestureDetector(ctx, mgl);
sgd = new ScaleGestureDetector(ctx, sgl);
rgd = new RotateGestureDetector(ctx, rgl);
}
class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
MoveGestureDetector.SimpleOnMoveGestureListener mgl = new MoveGestureDetector.SimpleOnMoveGestureListener() {
@Override
public boolean onMove(MoveGestureDetector detector) {
PointF delta = detector.getFocusDelta();
matrix.postTranslate(delta.x, delta.y);
invalidate();
return true;
}
};
ScaleGestureDetector.SimpleOnScaleGestureListener sgl = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
matrix.postScale(scale, scale, detector.getFocusX(), detector.getFocusY());
invalidate();
return true;
}
};
RotateGestureDetector.SimpleOnRotateGestureListener rgl = new RotateGestureDetector.SimpleOnRotateGestureListener() {
@Override
public boolean onRotate(RotateGestureDetector detector) {
matrix.postRotate(-detector.getRotationDegreesDelta(), detector.getFocusX(), detector.getFocusY());
invalidate();
return true;
}
};
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//view given size
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
private void touch_start(float x, float y) {
undonePaths.clear();
drawPath.reset();
drawPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y, float x2, float y2) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
/* QUad to curves using a quadratic line (basically an ellipse of some sort).
LineTo is a straight line. QuadTo will smooth out jaggedies where they turn.
*/
drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
drawPath.lineTo(mX, mY);
// commit the path to our offscreen
drawCanvas.drawPath(drawPath, drawPaint);
// kill this so we don't double draw
paths.add(drawPath);
drawPath = new Path();
drawPath.reset();
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isZoomable) {
mgd.onTouchEvent(event);
sgd.onTouchEvent(event);
rgd.onTouchEvent(event);
}
if (!isTouchable) {
return super.onTouchEvent(event);
} else {
//detect user touch
float x = event.getX();
float y = event.getY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
if (!isZoomable) {
touch_start(x, y);
}
invalidate();
break;
case MotionEvent.ACTION_MOVE:
if (!isZoomable) {
//mPositions.add(new Vector2(x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2));
if (isCustomBrush && mBitmapBrushDimensions != null) {
mPositions = new Vector2(x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2);
touch_move(x, y, x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2);
} else {
touch_move(x, y, 0, 0);
}
}
invalidate();
break;
case MotionEvent.ACTION_UP:
if (!isZoomable) {
touch_up();
}
invalidate();
break;
}
mScaleDetector.onTouchEvent(event);
return true;
}
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.setMatrix(matrix);
for (Path p : paths) {
canvas.drawPath(p, drawPaint);
drawPaint.setColor(selectedColor);
drawPaint.setStrokeWidth(brushSize);
canvas.drawPath(drawPath, drawPaint);
}
canvas.restore();
}
}
PS:MoveGestureDetector()、ScaleGestureDetector() 和 RotateGestureDetector() 是从 android-gesture-detectors 继承的自定义类
答:
1赞
Vyacheslav
9/3/2016
#1
要应用任何变换,您必须了解数学规则。它适用于 2dim 和 3 维图形。 也就是说,如果您使用平移 (T)、旋转 (R)、缩放 (S) 矩阵来应用任何变换,则首先缩放对象(将坐标 xyz 乘以该矩阵 S),然后旋转(乘以 R),然后将对象移动 T。 因此,您在某个点上应用旋转,您必须将对象移动到零并缩放,然后返回基点。 也就是说,在您的情况下,在应用刻度之前,您必须通过触摸位置移动(减少)所有坐标,然后通过乘法应用刻度矩阵,然后通过此触摸增加所有位置来移动。
评论
0赞
Falling Into Infinity
9/3/2016
你介意举一个代码示例吗?正如我之前提到的,这是一个后续问题,所以你可以给出你自己缩放和缩放画布的想法。
3赞
kburbach
9/10/2016
#2
这是我所做的。基本上,您必须找到“旧”点和新点之间的区别。跳到底部查看重要行...
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
float xDiff = initialFocalPoints[0] - currentFocalPoints[0];
float yDiff = initialFocalPoints[1] - currentFocalPoints[1];
transformMatrix.setScale(scaleFactor, scaleFactor,
currentFocalPoints[0], currentFocalPoints[1]);
transformMatrix.postTranslate(xDiff, yDiff);
child.setImageMatrix(transformMatrix);
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector){
float startX = detector.getFocusX() + getScrollX();
float startY = detector.getFocusY() + getScrollY();
initialFocalPoints = new float[]{startX, startY};
if(transformMatrix.invert(inverseTransformMatrix))
inverseTransformMatrix.mapPoints(currentFocalPoints, initialFocalPoints);
return true;
}
造成差异的线路如下:
float xDiff = initialFocalPoints[0] - currentFocalPoints[0];
float yDiff = initialFocalPoints[1] - currentFocalPoints[1];
transformMatrix.postTranslate(xDiff, yDiff);
答案很简单,就是弄清楚两点之间的差异,并在每次缩放图像时转换图像视图。
评论