10

d'abord tout cela est une question de suivi à l'origine posée ici, Pan, Zoom and Scale a custom View for Canvas drawing in Androidposition échelle incorrect après application de zoom sur toile Android

Comme il n'y avait pas encore de réponse, je me suis finalement résolu mon problème en utilisant la android-gesture-detectors

Après avoir appliqué le geste de zoom/échelle, j'ai trouvé que les coordonnées du dessin de toile pointent toujours sur l'ancienne position (avant d'appliquer le zoom) et ne dessinent pas exactement les mêmes coordonnées tactiles. Fondamentalement, je ne peux pas obtenir les coordonnées de toile correctes après la mise à l'échelle ou en faisant glisser le canevas.

Avant le zoom,

enter image description here

Après un zoom sur les points de contact sont dessin sur l'emplacement précédent. Je veux dessiner sur l'emplacement tactile en cours,

enter image description here

Exemple de code,

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() sont classes personnalisées héritées de android-gesture-detectors

Répondre

1

Pour appliquer une transformation, vous devez comprendre les règles des mathématiques. Il fonctionne à la fois pour les graphiques 2dim et 3 dimensions. Si vous travaillez avec des matrices de translation (T), de rotation (R), d'échelle (S) pour appliquer une transformation, vous avez d'abord un objet échelle (multipliez les coordonnées xyz par cette matrice S) puis tournez (mult. Ensuite, vous déplacez l'objet par T. Donc, vous appliquez la rotation sur un certain point, vous devez déplacer l'objet à zéro et l'échelle, puis revenir au point de base. C'est à dire dans votre cas, avant d'appliquer l'échelle, vous devez déplacer toutes les coordonnées par la position tactile puis appliquer la matrice d'échelle par multiplication puis changer en augmentant toutes les positions par ce contact contre.

+0

Pourriez-vous donner un exemple de code? Comme je l'ai mentionné plus tôt, c'est une question de suivi, donc vous pouvez donner votre propre idée de mise à l'échelle et zoom de la toile. –

2

Voici ce que j'ai fait. Fondamentalement, vous devez trouver la différence entre les «anciens» et les nouveaux points. Passer au fond pour les lignes importantes ...

@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; 
} 

Les lignes qui ont fait la différence sont les suivantes:

float xDiff = initialFocalPoints[0] - currentFocalPoints[0]; 
float yDiff = initialFocalPoints[1] - currentFocalPoints[1]; 
transformMatrix.postTranslate(xDiff, yDiff); 

La réponse est aussi simple que déterminer la différence entre les deux points et traduire l'imageview chaque fois que l'image est mise à l'échelle.