Android: 手绘在ImageView之上

Android: Hand drawing on top of ImageView

我想在 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>

我关注Multi-Touch Tracking in Android example HandDrawingCanvasView Class:

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

结果如下:

如您所见,我什至可以在图像外绘制(蓝色区域是 ImageView 背景)。如何将可绘制区域限制在图片边界内?

您首先需要在显示它的 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);
}

我在一个小示例项目上对其进行了测试,它工作正常。

希望对您有所帮助。