首先这是一个跟随问题,原来在这里提出,Pan,Zoom and Scale a custom View for Canvas drawing in Android
既然没有答案,我终于用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.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,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,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继承的自定义类
解决方法
这是我做的基本上你必须找到“旧”和新点之间的区别.跳到底部的重要线条…
@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);
答案很简单,就是找出两点之间的区别,并且每次图像缩放时都可以翻译图像视图.