0

我想在ImageView之上實現手繪功能。Android:在ImageView頂部繪製手形

這是我的佈局:

<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> 

我遵循了HandDrawingCanvasViewMulti-Touch Tracking in Android example

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

而且這些結果如下: portrait landscape

正如你可以看到,我甚至可以在圖像外面畫圖(藍色區域是t他ImageView背景)。如何限制可繪製區域只在圖片的邊界內?

回答

0

您首先需要在顯示它的ImageView內找到繪製圖像的邊界。

一旦你有了,你可以放棄任何超出該區域的觸摸事件。您onTouchEvent的僞代碼會在某個地方的路線:

Overrides 
    public override bool OnTouchEvent(MotionEvent touchEvent){ 

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

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

要找到一個ImageView繪製的可繪製的邊界,你可以使用Rect r = ImageView.getDrawable.copyBounds(),這將寫界限在r

最後,isInsideDesiredArea(...)看起來就像這樣:

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

我測試的一個小樣本項目,並能正常工作。

希望這會有所幫助。

+0

我已經試過您的解決方案和邊界區域轉移,你可以在這裏看到:https://imgur.com/a/d1IvO 然後,經過二/三次繪製,將有'異常InProgressPolylines.Add(id,polyline);' – currarpickt

+0

啊好的。您已經在視圖的本地座標系中獲取了觸摸事件。我會更新我的答案。我不能評論崩潰,但必須在你的代碼 – cjurjiu

+0

檢查新版本的代碼,應該現在確定。以前的版本也垂直翻譯事件,假設事件在屏幕的座標系中,以匹配圖像視圖的座標系統。但由於你的onTouchEvent是ImageView本身的,所以這個翻譯沒有必要 – cjurjiu