0

Je veux mettre en œuvre la fonction de dessin à la main sur un ImageView.Android: dessin à la main sur le dessus d'ImageView

C'est ma mise en page:

<android.support.constraint.ConstraintLayout 
     android:id="@+id/constraintLayoutEditImage" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     app:layout_behavior="@string/appbar_scrolling_view_behavior"> 
     <RelativeLayout 
      android:id="@+id/relativeLayoutEditImage" 
      android:layout_width="0dp" 
      android:layout_height="0dp" 
      android:layout_marginRight="0dp" 
      app:layout_constraintRight_toRightOf="parent" 
      android:layout_marginLeft="0dp" 
      app:layout_constraintLeft_toLeftOf="parent" 
      app:layout_constraintTop_toTopOf="parent" 
      android:layout_marginTop="0dp" 
      android:layout_marginBottom="0dp" 
      app:layout_constraintBottom_toTopOf="@+id/constraintLayoutEditImageToolbar"> 

      <ImageView 
       android:id="@+id/imageViewEditImage" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent" 
       android:background="@color/light_blue" /> 

      <mobileclient.Droid.HandDrawingCanvasView 
       android:id="@+id/canvasViewEditImage" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent" 
       android:background="@android:color/transparent"/> 
     </RelativeLayout> 

     <android.support.constraint.ConstraintLayout 
      ........> //this is the toolbar, etc 
     </android.support.constraint.ConstraintLayout> 
</android.support.constraint.ConstraintLayout> 

Je suis Multi-Touch Tracking in Android example pour la classe HandDrawingCanvasView:

public class HandDrawingCanvasView: View 
    { 
     // Two collections for storing polylines 
     Dictionary<int, HandDrawingPolyline> InProgressPolylines = new Dictionary<int, HandDrawingPolyline>(); 
     List<HandDrawingPolyline> CompletedPolylines = new List<HandDrawingPolyline>(); 

     Paint paint = new Paint(PaintFlags.AntiAlias); 

     public HandDrawingCanvasView(Context context) : base(context) 
     { 
      Initialize(); 
     } 

     public HandDrawingCanvasView(Context context, IAttributeSet attrs) : 
      base(context, attrs) 
     { 
      Initialize(); 
     } 

     void Initialize() 
     { 
     } 

     // External interface accessed from MainActivity 
     public Color StrokeColor { set; get; } = Color.Red; 

     public float StrokeWidth { set; get; } = 2; 

     public void ClearAll() 
     { 
      CompletedPolylines.Clear(); 
      Invalidate(); 
     } 

     // Overrides 
     public override bool OnTouchEvent(MotionEvent args) 
     { 
      // Get the pointer index 
      int pointerIndex = args.ActionIndex; 

      // Get the id to identify a finger over the course of its progress 
      int id = args.GetPointerId(pointerIndex); 

      // Use ActionMasked here rather than Action to reduce the number of possibilities 
      switch (args.ActionMasked) 
      { 
       case MotionEventActions.Down: 
       case MotionEventActions.PointerDown: 

        // Create a Polyline, set the initial point, and store it 
        HandDrawingPolyline polyline = new HandDrawingPolyline 
        { 
         Color = StrokeColor, 
         StrokeWidth = StrokeWidth 
        }; 

        polyline.Path.MoveTo(args.GetX(pointerIndex), 
             args.GetY(pointerIndex)); 

        InProgressPolylines.Add(id, polyline); 
        break; 

       case MotionEventActions.Move: 

        // Multiple Move events are bundled, so handle them differently 
        for (pointerIndex = 0; pointerIndex < args.PointerCount; pointerIndex++) 
        { 
         id = args.GetPointerId(pointerIndex); 

         InProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex), 
                  args.GetY(pointerIndex)); 
        } 
        break; 

       case MotionEventActions.Up: 
       case MotionEventActions.Pointer1Up: 

        InProgressPolylines[id].Path.LineTo(args.GetX(pointerIndex), 
                 args.GetY(pointerIndex)); 

        // Transfer the in-progress polyline to a completed polyline 
        CompletedPolylines.Add(InProgressPolylines[id]); 
        InProgressPolylines.Remove(id); 
        break; 

       case MotionEventActions.Cancel: 
        InProgressPolylines.Remove(id); 
        break; 
      } 

      // Invalidate to update the view 
      Invalidate(); 

      // Request continued touch input 
      return true; 
     } 

     protected override void OnDraw(Canvas canvas) 
     { 
      base.OnDraw(canvas); 

      // Clear canvas to white 
      paint.SetStyle(Paint.Style.Fill); 
      paint.Color = Color.Transparent; 
      canvas.DrawPaint(paint); 

      // Draw strokes 
      paint.SetStyle(Paint.Style.Stroke); 
      paint.StrokeCap = Paint.Cap.Round; 
      paint.StrokeJoin = Paint.Join.Round; 

      // Draw the completed polylines 
      foreach (HandDrawingPolyline polyline in CompletedPolylines) 
      { 
       paint.Color = polyline.Color; 
       paint.StrokeWidth = polyline.StrokeWidth; 
       canvas.DrawPath(polyline.Path, paint); 
      } 

      // Draw the in-progress polylines 
      foreach (HandDrawingPolyline polyline in InProgressPolylines.Values) 
      { 
       paint.Color = polyline.Color; 
       paint.StrokeWidth = polyline.StrokeWidth; 
       canvas.DrawPath(polyline.Path, paint); 
      } 
     } 
    } 

Voici les résultats: portrait landscape

Comme vous pouvez le voir, Je pourrais dessiner même en dehors de l'image (la zone bleue est t il ImageView arrière-plan). Comment limiter la zone dessinable à seulement dans les limites de l'image?

Répondre

0

Vous devez d'abord trouver les limites de l'image dessinée dans le ImageView qui l'affiche.

Une fois que vous avez cela, vous pouvez ignorer tout événement tactile qui sort de cette zone. Pseudo code de votre onTouchEvent serait quelque part dans les lignes de:

Overrides 
    public override bool OnTouchEvent(MotionEvent touchEvent){ 

     if(!isInsideDesiredArea(touchEvent.getX(), touchEvent.getY()){ 
      return false; 
     } 

     ... //same with what you have now 
    } 

Pour trouver les limites d'un drawable dessiné par un ImageView, vous pouvez utiliser Rect r = ImageView.getDrawable.copyBounds(), qui rédigera les limites dans r.

Enfin, isInsideDesiredArea(...) ressemblera à ceci:

private boolean isInsideDesiredArea(float x, float y) { 
    //get the image view 
    ImageView imageView = (ImageView) findViewById(R.id.my_imageview_id); 

    //get the transform it applied on the drawable 
    float[] imageViewTransformMatrixValues = new float[9]; 
    imageView.getImageMatrix().getValues(imageViewTransformMatrixValues); 

    //get the bounds of the drawn drawable, before the transforms are applied to it, and in local coordinates 
    Rect drawableRect = imageView.getDrawable().copyBounds(); 

    //get the drawable scale & translation, from its matrix 
    float scaleX = imageViewTransformMatrixValues[Matrix.MSCALE_X]; 
    float scaleY = imageViewTransformMatrixValues[Matrix.MSCALE_Y]; 
    float translationX = imageViewTransformMatrixValues[Matrix.MTRANS_X]; 
    float translationY = imageViewTransformMatrixValues[Matrix.MTRANS_Y]; 

    //compute the actual bounds of the drawable, within the image view, in image view local coordinates 
    Rect actualImageRect = new Rect(); 
    actualImageRect.top = Math.round(translationY + scaleY * drawableRect.top); 
    actualImageRect.left = Math.round(translationX + scaleX * drawableRect.left); 
    actualImageRect.bottom = Math.round(translationY + scaleY * drawableRect.bottom); 
    actualImageRect.right = Math.round(translationX + scaleX * drawableRect.right); 

    //finally check if the touch events are within the rectangle defined by the drawable 
    return actualImageRect.contains((int) x, (int) y); 
} 

Je l'ai testé sur un petit projet de l'échantillon, et il fonctionne très bien.

Espérons que cela aide.

+0

J'ai essayé votre solution et la zone des limites est décalée comme vous pouvez le voir ici: https://imgur.com/a/d1IvO puis, après deux/trois fois de dessin, il y aura une exception dans ' InProgressPolylines.Add (id, polyline); ' – currarpickt

+0

ah ok. vous obtenez déjà les événements tactiles dans le système de coordonnées local de la vue. Je mettrai à jour ma réponse. Je ne peux pas commenter sur le crash cependant, cela doit être quelque part dans votre code – cjurjiu

+0

vérifier la nouvelle version du code, devrait être ok maintenant. la version précédente traduisait également les événements verticalement, en supposant que les événements se trouvaient dans le système de coordonnées de l'écran, afin de correspondre au système de coordonnées de la vue d'image. mais puisque votre onTouchEvent est de l'ImageView lui-même, cette traduction n'était pas nécessaire – cjurjiu