How to create a resizable rectangle with user touch events on Android?

核能气质少年 提交于 2019-12-17 02:42:08

问题


I want to create a rectangular shape that will be resized with the touches of the user. Below image is a good example of what i want to do:

Is there any example like that? What do I need to study to implement this?

Thanks in advance,


回答1:


To implement a custom view, you derive a class from View :) Override onDraw() for looks, override onTouchEvent() for input processing. Note that in Android, you cannot draw on view outside onDraw(); if you want to refresh the view, call invalidate().

You can implement draggable corners as separate views. For looks, just use ready-made images (feel free to derive from ImageView). Dragging is implemented as moving your view in response to touch events. RelativeLayout is your friend for arbitrary view positining.

You can add homemade views to the layout; just go to XML editing and type a <com.mypackage.MyViewClass> element.




回答2:


Chintan Rathod's answer was great solution but there are something wrong when He draws the rectangle. I just edit some lines of code to make it works correctly with user touch event. Now, you can add this view to your layout then touch to draw.

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.ihnel.englishpronounciation.R;

public class DrawView extends View {

    Point[] points = new Point[4];

    /**
     * point1 and point 3 are of same group and same as point 2 and point4
     */
    int groupId = -1;
    private ArrayList<ColorBall> colorballs = new ArrayList<ColorBall>();
    // array that holds the balls
    private int balID = 0;
    // variable to know what ball is being dragged
    Paint paint;
    Canvas canvas;

    public DrawView(Context context) {
        super(context);
        paint = new Paint();
        setFocusable(true); // necessary for getting the touch events
        canvas = new Canvas();
    }

    public DrawView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public DrawView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        setFocusable(true); // necessary for getting the touch events
        canvas = new Canvas();
    }

    // the method that draws the balls
    @Override
    protected void onDraw(Canvas canvas) {
        if(points[3]==null) //point4 null when user did not touch and move on screen.
            return;
        int left, top, right, bottom;
        left = points[0].x;
        top = points[0].y;
        right = points[0].x;
        bottom = points[0].y;
        for (int i = 1; i < points.length; i++) {
            left = left > points[i].x ? points[i].x:left;
            top = top > points[i].y ? points[i].y:top;
            right = right < points[i].x ? points[i].x:right;
            bottom = bottom < points[i].y ? points[i].y:bottom;
        }
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeWidth(5);

        //draw stroke
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.parseColor("#AADB1255"));
        paint.setStrokeWidth(2);
        canvas.drawRect(
                    left + colorballs.get(0).getWidthOfBall() / 2,
                    top + colorballs.get(0).getWidthOfBall() / 2, 
                    right + colorballs.get(2).getWidthOfBall() / 2, 
                    bottom + colorballs.get(2).getWidthOfBall() / 2, paint);
        //fill the rectangle
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.parseColor("#55DB1255"));
        paint.setStrokeWidth(0);
        canvas.drawRect(
                left + colorballs.get(0).getWidthOfBall() / 2,
                top + colorballs.get(0).getWidthOfBall() / 2, 
                right + colorballs.get(2).getWidthOfBall() / 2, 
                bottom + colorballs.get(2).getWidthOfBall() / 2, paint);

        //draw the corners
        BitmapDrawable bitmap = new BitmapDrawable();
        // draw the balls on the canvas
        paint.setColor(Color.BLUE);
        paint.setTextSize(18);
        paint.setStrokeWidth(0);
        for (int i =0; i < colorballs.size(); i ++) {
            ColorBall ball = colorballs.get(i);
            canvas.drawBitmap(ball.getBitmap(), ball.getX(), ball.getY(),
                    paint);

            canvas.drawText("" + (i+1), ball.getX(), ball.getY(), paint);
        }
    }

    // events when touching the screen
    public boolean onTouchEvent(MotionEvent event) {
        int eventaction = event.getAction();

        int X = (int) event.getX();
        int Y = (int) event.getY();

        switch (eventaction) {

        case MotionEvent.ACTION_DOWN: // touch down so check if the finger is on
                                        // a ball
            if (points[0] == null) {
                //initialize rectangle.
                points[0] = new Point();
                points[0].x = X;
                points[0].y = Y;

                points[1] = new Point();
                points[1].x = X;
                points[1].y = Y + 30;

                points[2] = new Point();
                points[2].x = X + 30;
                points[2].y = Y + 30;

                points[3] = new Point();
                points[3].x = X +30;
                points[3].y = Y;

                balID = 2;
                groupId = 1;
                 // declare each ball with the ColorBall class
                for (Point pt : points) {
                     colorballs.add(new ColorBall(getContext(), R.drawable.ic_circle, pt));
                }
            } else {
                //resize rectangle
                balID = -1;
                groupId = -1;
                for (int i = colorballs.size()-1; i>=0; i--) {
                    ColorBall ball = colorballs.get(i);
                    // check if inside the bounds of the ball (circle)
                    // get the center for the ball
                    int centerX = ball.getX() + ball.getWidthOfBall();
                    int centerY = ball.getY() + ball.getHeightOfBall();
                    paint.setColor(Color.CYAN);
                    // calculate the radius from the touch to the center of the
                    // ball
                    double radCircle = Math
                            .sqrt((double) (((centerX - X) * (centerX - X)) + (centerY - Y)
                                    * (centerY - Y)));

                    if (radCircle < ball.getWidthOfBall()) {

                        balID = ball.getID();
                        if (balID == 1 || balID == 3) {
                            groupId = 2;
                        } else {
                            groupId = 1;
                        }
                        invalidate();
                        break;
                    }
                    invalidate();
                }
            }
            break;

        case MotionEvent.ACTION_MOVE: // touch drag with the ball


            if (balID > -1) {
                // move the balls the same as the finger
                colorballs.get(balID).setX(X);
                colorballs.get(balID).setY(Y);

                paint.setColor(Color.CYAN);
                if (groupId == 1) {
                    colorballs.get(1).setX(colorballs.get(0).getX());
                    colorballs.get(1).setY(colorballs.get(2).getY());
                    colorballs.get(3).setX(colorballs.get(2).getX());
                    colorballs.get(3).setY(colorballs.get(0).getY());
                } else {
                    colorballs.get(0).setX(colorballs.get(1).getX());
                    colorballs.get(0).setY(colorballs.get(3).getY());
                    colorballs.get(2).setX(colorballs.get(3).getX());
                    colorballs.get(2).setY(colorballs.get(1).getY());
                }

                invalidate();
            }

            break;

        case MotionEvent.ACTION_UP:
            // touch drop - just do things here after dropping

            break;
        }
        // redraw the canvas
        invalidate();
        return true;

    }


    public static class ColorBall {

        Bitmap bitmap;
        Context mContext;
        Point point;
        int id;
        static int count = 0;

        public ColorBall(Context context, int resourceId, Point point) {
            this.id = count++;
            bitmap = BitmapFactory.decodeResource(context.getResources(),
                    resourceId);
            mContext = context;
            this.point = point;
        }

        public int getWidthOfBall() {
            return bitmap.getWidth();
        }

        public int getHeightOfBall() {
            return bitmap.getHeight();
        }

        public Bitmap getBitmap() {
            return bitmap;
        }

        public int getX() {
            return point.x;
        }

        public int getY() {
            return point.y;
        }

        public int getID() {
            return id;
        }

        public void setX(int x) {
            point.x = x;
        }

        public void setY(int y) {
            point.y = y;
        }
    }
}



回答3:


Following code is to draw rectangle on touch base.

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.common.Utils;
import com.example.rectangleoverlay.R;

public class DrawView extends View {

    Point point1, point3;
    Point point2, point4;

    /**
     * point1 and point 3 are of same group and same as point 2 and point4
     */
    int groupId = -1;
    private ArrayList<ColorBall> colorballs = new ArrayList<ColorBall>();
    // array that holds the balls
    private int balID = 0;
    // variable to know what ball is being dragged
    Paint paint;
    Canvas canvas;

    public DrawView(Context context) {
        super(context);
        paint = new Paint();
        setFocusable(true); // necessary for getting the touch events
        canvas = new Canvas();
        // setting the start point for the balls
        point1 = new Point();
        point1.x = 50;
        point1.y = 20;

        point2 = new Point();
        point2.x = 150;
        point2.y = 20;

        point3 = new Point();
        point3.x = 150;
        point3.y = 120;

        point4 = new Point();
        point4.x = 50;
        point4.y = 120;

        // declare each ball with the ColorBall class
        colorballs.add(new ColorBall(context, R.drawable.gray_circle, point1));
        colorballs.add(new ColorBall(context, R.drawable.gray_circle, point2));
        colorballs.add(new ColorBall(context, R.drawable.gray_circle, point3));
        colorballs.add(new ColorBall(context, R.drawable.gray_circle, point4));

    }

    public DrawView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public DrawView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        setFocusable(true); // necessary for getting the touch events
        canvas = new Canvas();
        // setting the start point for the balls
        point1 = new Point();
        point1.x = 50;
        point1.y = 20;

        point2 = new Point();
        point2.x = 150;
        point2.y = 20;

        point3 = new Point();
        point3.x = 150;
        point3.y = 120;

        point4 = new Point();
        point4.x = 50;
        point4.y = 120;

        // declare each ball with the ColorBall class
        colorballs.add(new ColorBall(context, R.drawable.gray_circle, point1));
        colorballs.add(new ColorBall(context, R.drawable.gray_circle, point2));
        colorballs.add(new ColorBall(context, R.drawable.gray_circle, point3));
        colorballs.add(new ColorBall(context, R.drawable.gray_circle, point4));

    }

    // the method that draws the balls
    @Override
    protected void onDraw(Canvas canvas) {
        // canvas.drawColor(0xFFCCCCCC); //if you want another background color

        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setColor(Color.parseColor("#55000000"));
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeJoin(Paint.Join.ROUND);
        // mPaint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeWidth(5);

        canvas.drawPaint(paint);
        paint.setColor(Color.parseColor("#55FFFFFF"));

        if (groupId == 1) {
            canvas.drawRect(point1.x + colorballs.get(0).getWidthOfBall() / 2,
                    point3.y + colorballs.get(2).getWidthOfBall() / 2, point3.x
                            + colorballs.get(2).getWidthOfBall() / 2, point1.y
                            + colorballs.get(0).getWidthOfBall() / 2, paint);
        } else {
            canvas.drawRect(point2.x + colorballs.get(1).getWidthOfBall() / 2,
                    point4.y + colorballs.get(3).getWidthOfBall() / 2, point4.x
                            + colorballs.get(3).getWidthOfBall() / 2, point2.y
                            + colorballs.get(1).getWidthOfBall() / 2, paint);
        }
        BitmapDrawable mBitmap;
        mBitmap = new BitmapDrawable();

        // draw the balls on the canvas
        for (ColorBall ball : colorballs) {
            canvas.drawBitmap(ball.getBitmap(), ball.getX(), ball.getY(),
                    new Paint());
        }
    }

    // events when touching the screen
    public boolean onTouchEvent(MotionEvent event) {
        int eventaction = event.getAction();

        int X = (int) event.getX();
        int Y = (int) event.getY();

        switch (eventaction) {

        case MotionEvent.ACTION_DOWN: // touch down so check if the finger is on
                                        // a ball
            balID = -1;
            groupId = -1;
            for (ColorBall ball : colorballs) {
                // check if inside the bounds of the ball (circle)
                // get the center for the ball
                Utils.logd("Id : " + ball.getID());
                Utils.logd("getX : " + ball.getX() + " getY() : " + ball.getY());
                int centerX = ball.getX() + ball.getWidthOfBall();
                int centerY = ball.getY() + ball.getHeightOfBall();
                paint.setColor(Color.CYAN);
                // calculate the radius from the touch to the center of the ball
                double radCircle = Math
                        .sqrt((double) (((centerX - X) * (centerX - X)) + (centerY - Y)
                                * (centerY - Y)));

                Utils.logd("X : " + X + " Y : " + Y + " centerX : " + centerX
                        + " CenterY : " + centerY + " radCircle : " + radCircle);

                if (radCircle < ball.getWidthOfBall()) {

                    balID = ball.getID();
                    Utils.logd("Selected ball : " + balID);
                    if (balID == 1 || balID == 3) {
                        groupId = 2;
                        canvas.drawRect(point1.x, point3.y, point3.x, point1.y,
                                paint);
                    } else {
                        groupId = 1;
                        canvas.drawRect(point2.x, point4.y, point4.x, point2.y,
                                paint);
                    }
                    invalidate();
                    break;
                }
                invalidate();
            }

            break;

        case MotionEvent.ACTION_MOVE: // touch drag with the ball
            // move the balls the same as the finger
            if (balID > -1) {
                Utils.logd("Moving Ball : " + balID);

                colorballs.get(balID).setX(X);
                colorballs.get(balID).setY(Y);

                paint.setColor(Color.CYAN);

                if (groupId == 1) {
                    colorballs.get(1).setX(colorballs.get(0).getX());
                    colorballs.get(1).setY(colorballs.get(2).getY());
                    colorballs.get(3).setX(colorballs.get(2).getX());
                    colorballs.get(3).setY(colorballs.get(0).getY());
                    canvas.drawRect(point1.x, point3.y, point3.x, point1.y,
                            paint);
                } else {
                    colorballs.get(0).setX(colorballs.get(1).getX());
                    colorballs.get(0).setY(colorballs.get(3).getY());
                    colorballs.get(2).setX(colorballs.get(3).getX());
                    colorballs.get(2).setY(colorballs.get(1).getY());
                    canvas.drawRect(point2.x, point4.y, point4.x, point2.y,
                            paint);
                }

                invalidate();
            }

            break;

        case MotionEvent.ACTION_UP:
            // touch drop - just do things here after dropping

            break;
        }
        // redraw the canvas
        invalidate();
        return true;

    }

    public void shade_region_between_points() {
        canvas.drawRect(point1.x, point3.y, point3.x, point1.y, paint);
    }
}

Following class is used to store objects

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;

public class ColorBall {

    Bitmap bitmap;
    Context mContext;
    Point point;
    int id;
    static int count = 0;

    public ColorBall(Context context, int resourceId, Point point) {
        this.id = count++;
        bitmap = BitmapFactory.decodeResource(context.getResources(),
                resourceId);
        mContext = context;
        this.point = point;
    }

    public int getWidthOfBall() {
        return bitmap.getWidth();
    }

    public int getHeightOfBall() {
        return bitmap.getHeight();
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public int getX() {
        return point.x;
    }

    public int getY() {
        return point.y;
    }

    public int getID() {
        return id;
    }

    public void setX(int x) {
        point.x = x;
    }

    public void setY(int y) {
        point.y = y;
    }
}



回答4:


The following code is a C# version (I am currently developing app on MonoDroid/Xamarin) of the requested code, but with some improvements and the ability to drag the rectangle. Still want to add some features, will edit it later.

namespace ImagePlayground
{
[Activity (Label = "MyView2")]          
public class MyView2 : View
{
    Graphics.Point[] points = new Graphics.Point[4];

    // Array that hold the circle
    private List<ResizeCircle> circles = new List<ResizeCircle>();

    // Variable to keep tracking of which circle is being dragged
    private int circleId = -1;

    // Points are grouped in groups of two so there's always only one fixed point       
    // groupId = 0 > Touch Inside the Rectangle
    // groupId = 1 > Points 0 and 2
    // groupId = 2 > Points 1 and 3
    int groupId = -1;

    // FirstTouch's Coordinate for Tracking on Dragging
    int xFirstTouch = 0;
    int yFirstTouch = 0;

    /** Main Bitmap **/
    private Bitmap mBitmap = null;

    /** Measured Size of the View **/
    private Rect mMeasuredRect;

    /** Paint to Draw Rectangles **/
    private Paint mRectPaint;

    public MyView2(Context ctx) : base (ctx){
        init (ctx);
    }

    public MyView2 (Context ctx, IAttributeSet attrs) : base (ctx, attrs){
        init (ctx);
    }

    public MyView2 (Context ctx, IAttributeSet attrs, int defStyle) : base(ctx,attrs,defStyle){
        init (ctx);
    }

    private void init(Context ctx){
        // For Touch Events
        Focusable = true;
        // Draw the Image on the Background
        mBitmap = BitmapFactory.DecodeResource(ctx.Resources, Resource.Drawable.bg);

        // Sets up the paint for the Drawable Rectangles
        mRectPaint = new Paint ();
        mRectPaint.Color = Android.Graphics.Color.Aqua;
        mRectPaint.StrokeWidth = 4;
        mRectPaint.SetStyle (Paint.Style.Stroke);
    }

    protected override void OnDraw(Canvas canvas){
        // Background Bitmap to Cover all Area
        canvas.DrawBitmap(mBitmap, null, mMeasuredRect, null);

        // Just draw the points only if it has already been initiated
        if (points [3] != null) {               
            int left, top, right, bottom;
            left = points [0].X;
            top = points [0].Y;
            right = points [0].X;
            bottom = points [0].Y;

            // Sets the circles' locations
            for (int i = 1; i < points.Length; i++) {
                left = left > points [i].X ? points [i].X : left;
                top = top > points [i].Y ? points [i].Y : top;
                right = right < points [i].X ? points [i].X : right;
                bottom = bottom < points [i].Y ? points [i].Y : bottom;
            }

            mRectPaint.AntiAlias = true;
            mRectPaint.Dither = true;
            mRectPaint.StrokeJoin = Paint.Join.Round;
            mRectPaint.StrokeWidth = 5;
            mRectPaint.SetStyle (Paint.Style.Stroke);
            mRectPaint.Color = Graphics.Color.ParseColor ("#0079A3");

            canvas.DrawRect (
                left + circles [0].GetCircleWidth () / 2,
                top + circles [0].GetCircleWidth () / 2, 
                right + circles [2].GetCircleWidth () / 2, 
                bottom + circles [2].GetCircleWidth () / 2, mRectPaint);

            // Fill The Rectangle
            mRectPaint.SetStyle (Paint.Style.Fill);
            mRectPaint.Color = Graphics.Color.ParseColor ("#B2D6E3");
            mRectPaint.Alpha = 75;
            mRectPaint.StrokeWidth = 0;

            canvas.DrawRect (
                left + circles [0].GetCircleWidth () / 2,
                top + circles [0].GetCircleWidth () / 2, 
                right + circles [2].GetCircleWidth () / 2, 
                bottom + circles [2].GetCircleWidth () / 2, mRectPaint);

            // DEBUG
            mRectPaint.Color = Graphics.Color.Red;
            mRectPaint.TextSize = 18;
            mRectPaint.StrokeWidth = 0;

            // Draw every circle on the right position
            for (int i = 0; i < circles.Count (); i++) {
                ResizeCircle circle = circles [i];
                float x = circle.GetX ();
                float y = circle.GetY ();
                canvas.DrawBitmap (circle.GetBitmap (), x, y,
                    mRectPaint);

                // DEBUG
  //                    canvas.DrawText ("" + (i + 1), circle.GetX (), circle.GetY (), mRectPaint);
            }
        }
    }

    public override bool OnTouchEvent(MotionEvent e){

        // Get the Coordinates of Touch
        int xTouch = (int) e.GetX ();
        int yTouch = (int) e.GetY ();
        int actionIndex = e.ActionIndex;

        switch (e.ActionMasked) {
        // In case user touch the screen
        case MotionEventActions.Down:

            // If no points were created
            if (points [0] == null) {
                // Offset to create the points
                int offset = 60;
                // Initialize a new Rectangle.
                points [0] = new Graphics.Point ();
                points [0].X = xTouch;
                points [0].Y = yTouch;

                points [1] = new Graphics.Point ();
                points [1].X = xTouch;
                points [1].Y = yTouch + offset;

                points [2] = new Graphics.Point ();
                points [2].X = xTouch + offset;
                points [2].Y = yTouch + offset;

                points [3] = new Graphics.Point ();
                points [3].X = xTouch + offset;
                points [3].Y = yTouch;

                // Add each circle to circles array
                foreach (Graphics.Point pt in points) {
                    circles.Add (new ResizeCircle (Context, Resource.Drawable.circle, pt));
                }
            } else {
                // Register Which Circle (if any) th user has touched
                groupId = getTouchedCircle (xTouch, yTouch);
                xFirstTouch = xTouch;
                yFirstTouch = yTouch;

            }
            break;
        case MotionEventActions.PointerDown:
            break;

        case MotionEventActions.Move:                                               
            if (groupId == 1 || groupId == 2) {

                // Move touched Circle as the finger moves
                circles[circleId].SetX(xTouch);
                circles[circleId].SetY(yTouch);

                // Move the two other circles accordingly
                if (groupId == 1) {
                    circles[1].SetX(circles[0].GetX());
                    circles[1].SetY(circles[2].GetY());
                    circles[3].SetX(circles[2].GetX());
                    circles[3].SetY(circles[0].GetY());
                } else {                        
                    circles[0].SetX(circles[1].GetX());
                    circles[0].SetY(circles[3].GetY());
                    circles[2].SetX(circles[3].GetX());
                    circles[2].SetY(circles[1].GetY());
                }
                Invalidate();
            } else if (groupId == 0){
                // Calculate the delta for the dragging
                int xDelta =  (xTouch-xFirstTouch);
                int yDelta =  (yTouch-yFirstTouch);
                xFirstTouch = xTouch;
                yFirstTouch = yTouch;

                // Move each circle accordingly
                foreach (ResizeCircle circle in circles) {
                    circle.SetX (circle.GetX () + xDelta);
                    circle.SetY (circle.GetY () + yDelta);
                }

                // Redraw the view
                Invalidate ();              
            }
            break;

        case MotionEventActions.Up:
            break;
        default:
            break;          
        }
        Invalidate ();
        return true;
    }


    private int getTouchedCircle(int xTouch, int yTouch){
        int groupId = -1;
        for (int i = 0; i < circles.Count; i++) {
            ResizeCircle circle = circles [i];

            // Check if the touch was inside the bounds of the circle
            int centerX = circle.GetX () + circle.GetCircleWidth ();
            int centerY = circle.GetY () + circle.GetCircleHeight ();

            // Calculate the radius from the touch to the center of the circle
            double radCircle = Math.Sqrt ((double)(((centerX - xTouch) * (centerX - xTouch)) + (centerY - yTouch)
                * (centerY - yTouch)));

            // If the touch was on one of the circles
            if (radCircle < circle.GetCircleWidth ()) {
                circleId = circle.GetID ();
                if (circleId == 1 || circleId == 3) {
                    groupId = 2;
                    break;
                } else {
                    groupId = 1;
                    break;
                }
            } else {
                // User didn't touch any of the circles nor the inside area
                groupId = -1;
            }
        }
        // If the touch wasn't on one of the circles, check if it was inside the rectangle
        if (groupId == -1) {
            List<int> xCoords = new List<int> ();
            List<int> yCoords = new List<int> ();

            // Gather Coordinates from all circles      
            foreach (ResizeCircle circle in circles){
                xCoords.Add (circle.GetX());
                yCoords.Add (circle.GetY());
            }

            // Store the max and min coordinates
            int minX = xCoords.Min ();
            int maxX = xCoords.Max ();
            int minY = yCoords.Min ();
            int maxY = yCoords.Max ();

            // Check if user has touched inside the rectangle
            if ((xTouch > minX && xTouch < maxX) && (yTouch > minY && yTouch < maxY)) {
                // User has touched inside the Rectangle
                groupId = 0;
            }
        }

        return groupId;
    }

    protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec){
        base.OnMeasure (widthMeasureSpec, heightMeasureSpec);

        mMeasuredRect = new Rect (0, 0, MeasuredWidth, MeasuredHeight);
    }

    public class ResizeCircle {

        Bitmap bitmap;
        Graphics.Point point;
        int id;
        static int count = 0;

        public ResizeCircle(Context context, int resourceId, Graphics.Point point) {
            this.id = count++;
            bitmap = BitmapFactory.DecodeResource(context.Resources,
                resourceId);
            Log.Debug("BITMAP" , bitmap.Height.ToString());
            this.point = point;
        }

        public int GetCircleWidth() {
            return bitmap.Width;
        }

        public int GetCircleHeight() {
            return bitmap.Height;
        }

        public Bitmap GetBitmap() {
            return bitmap;
        }

        public int GetX() {
            return point.X;
        }

        public int GetY() {
            return point.Y;
        }

        public int GetID() {
            return id;
        }

        public void SetX(int x) {
            point.X = x;
        }

        public void SetY(int y) {
            point.Y = y;
        }
    }
   }
  }



回答5:


The edited answer of Nguyen Minh Binh worked for me. But I needed to add a lite fix to prevent the ball id to get out of range. This occurred for me if I had to reopen the activity hosting the custom view. I fixed these line:

this.id = count++;

to:

if (count > 3) count = 0; 
this.id = count++;



回答6:


Working Demonstration https://www.youtube.com/watch?v=BfYd7Xa-tCc

I wasn't happy with the highest rated answers for a couple of reasons.

1) they were not easily usable as a view in xml--the attributes are missing so the view is not easily recyclable.

2) it seemed silly to be making bitmaps when dropping in a drawable in xml is easier

3) they don't account for the edges of the view

4) they don't prevent inversion of the rectangle by dragging too far to the left or top

5) they don't account for the offset from the touch position to the center of the corner points

6) they both depend on a depreciated bitmap method

7) and most importantly, I couldn't get either of them to actually work

I grant that my solution is specialized to a square view, but it can be adjusted fairly easily to have a minimum X, and minimum Y side.

place this in the res/values folder:

custom_attributes.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="IconCropView">
        <attr name="minimumSide" format="dimension"/>
        <attr name="resizeCornerDrawable" format="reference"/>
        <attr name="moveCornerDrawable" format="reference"/>
        <attr name="cornerColor" format="color"/>
        <attr name="edgeColor" format="color" />
        <attr name="outsideCropColor" format="color" />
        <attr name="cornerSize" format="dimension" />
    </declare-styleable>
</resources>

Add this to a java package IconCropView.java

package your_package;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import your_package.R;

public class IconCropView extends View {

//contants strings
private static final String TAG = "IconCropView";

//drawing objects
private Paint paint;

//point objects
private Point[] points;
private Point start;
private Point offset;

//variable ints
private int minimumSideLength;
private int side;
private int halfCorner;
private int cornerColor;
private int edgeColor;
private int outsideColor;
private int corner = 5;

//variable booleans
private boolean initialized = false;

//drawables
private Drawable moveDrawable;
private Drawable resizeDrawable1, resizeDrawable2, resizeDrawable3;

//context
Context context;

public IconCropView(Context context) {
    super(context);
    this.context = context;
    init(null);
}

public IconCropView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    init(attrs);
}

public IconCropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.context = context;
    init(attrs);
}

public IconCropView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    this.context = context;
    init(attrs);
}

private void init(@Nullable AttributeSet attrs){

    paint = new Paint();
    start = new Point();
    offset = new Point();

    TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.IconCropView, 0, 0);

    //initial dimensions
    minimumSideLength = ta.getDimensionPixelSize(R.styleable.IconCropView_minimumSide, 20);
    side = minimumSideLength;
    halfCorner = (ta.getDimensionPixelSize(R.styleable.IconCropView_cornerSize, 20))/2;

    //colors
    cornerColor = ta.getColor(R.styleable.IconCropView_cornerColor, Color.BLACK);
    edgeColor = ta.getColor(R.styleable.IconCropView_edgeColor, Color.WHITE);
    outsideColor = ta.getColor(R.styleable.IconCropView_outsideCropColor, Color.parseColor("#00000088"));

    //initialize corners;
    points = new Point[4];

    points[0] = new Point();
    points[1] = new Point();
    points[2] = new Point();
    points[3] = new Point();

    //init corner locations;
    //top left
    points[0].x = 0;
    points[0].y = 0;

    //top right
    points[1].x = minimumSideLength;
    points[1].y = 0;

    //bottom left
    points[2].x = 0;
    points[2].y = minimumSideLength;

    //bottom right
    points[3].x = minimumSideLength;
    points[3].y = minimumSideLength;

    //init drawables
    moveDrawable = ta.getDrawable(R.styleable.IconCropView_moveCornerDrawable);
    resizeDrawable1 = ta.getDrawable(R.styleable.IconCropView_resizeCornerDrawable);
    resizeDrawable2 = ta.getDrawable(R.styleable.IconCropView_resizeCornerDrawable);
    resizeDrawable3 = ta.getDrawable(R.styleable.IconCropView_resizeCornerDrawable);

    //set drawable colors
    moveDrawable.setTint(cornerColor);
    resizeDrawable1.setTint(cornerColor);
    resizeDrawable2.setTint(cornerColor);
    resizeDrawable3.setTint(cornerColor);

    //recycle attributes
    ta.recycle();

    //set initialized to true
    initialized = true;

}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //set paint to draw edge, stroke
    if(initialized) {
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setColor(edgeColor);
        paint.setStrokeWidth(4);

        //crop rectangle
        canvas.drawRect(points[0].x + halfCorner,points[0].y + halfCorner, points[0].x + halfCorner + side, points[0].y + halfCorner + side, paint);

        //set paint to draw outside color, fill
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(outsideColor);

        //top rectangle
        canvas.drawRect(0, 0, canvas.getWidth(), points[0].y + halfCorner, paint);
        //left rectangle
        canvas.drawRect(0, points[0].y + halfCorner, points[0].x + halfCorner, canvas.getHeight(), paint);
        //right rectangle
        canvas.drawRect(points[0].x + halfCorner + side, points[0].y + halfCorner, canvas.getWidth(), points[0].y + halfCorner + side, paint);
        //bottom rectangle
        canvas.drawRect(points[0].x + halfCorner, points[0].y + halfCorner + side, canvas.getWidth(), canvas.getHeight(), paint);

        //set bounds of drawables
        moveDrawable.setBounds(points[0].x, points[0].y, points[0].x + halfCorner*2, points[0].y + halfCorner*2);
        resizeDrawable1.setBounds(points[1].x, points[1].y, points[1].x + halfCorner*2, points[1].y + halfCorner*2);
        resizeDrawable2.setBounds(points[2].x, points[2].y, points[2].x + halfCorner*2, points[2].y + halfCorner*2);
        resizeDrawable3.setBounds(points[3].x, points[3].y, points[3].x + halfCorner*2, points[3].y+ halfCorner*2);

        //place corner drawables
        moveDrawable.draw(canvas);
        resizeDrawable1.draw(canvas);
        resizeDrawable2.draw(canvas);
        resizeDrawable3.draw(canvas);

    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    //return super.onTouchEvent(event);
    switch(event.getActionMasked()){
        case MotionEvent.ACTION_DOWN:{

            //get the coordinates
            start.x = (int)event.getX();
            start.y = (int)event.getY();

            //get the corner touched if any
            corner = getCorner(start.x, start.y);

            //get the offset of touch(x,y) from corner top-left point
            offset = getOffset(start.x, start.y, corner);

            //account for touch offset in starting point
            start.x = start.x - offset.x;
            start.y = start.y - offset.y;

            break;
        }
        case MotionEvent.ACTION_UP:{

        }
        case MotionEvent.ACTION_MOVE:{
            if(corner == 0) {
                points[0].x = Math.max(points[0].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[0].x - 2*halfCorner - side)), 0);
                points[1].x = Math.max(points[1].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[1].x - 2*halfCorner)), side);
                points[2].x = Math.max(points[2].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[2].x - 2*halfCorner - side)), 0);
                points[3].x = Math.max(points[3].x + (int) Math.min(Math.floor((event.getX() - start.x - offset.x)), Math.floor(getWidth() - points[3].x - 2*halfCorner)), side);

                points[0].y = Math.max(points[0].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[0].y - 2*halfCorner - side)), 0);
                points[1].y = Math.max(points[1].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[1].y - 2*halfCorner - side)), 0);
                points[2].y = Math.max(points[2].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[2].y - 2*halfCorner)), side);
                points[3].y = Math.max(points[3].y + (int) Math.min(Math.floor((event.getY() - start.y - offset.y)), Math.floor(getHeight() - points[3].y - 2*halfCorner)), side);

                start.x = points[0].x;
                start.y = points[0].y;
                invalidate();
            }else if (corner == 1){
                side = Math.min((Math.min((Math.max(minimumSideLength, (int)(side + Math.floor(event.getX()) - start.x - offset.x))), side + (getWidth() - points[1].x - 2* halfCorner ))),side + (getHeight() - points[2].y - 2* halfCorner ));
                points[1].x = points[0].x + side;
                points[3].x = points[0].x + side;
                points[3].y = points[0].y + side;
                points[2].y = points[0].y + side;
                start.x = points[1].x;
                invalidate();
            }else if (corner == 2){
                side =  Math.min((Math.min((Math.max(minimumSideLength, (int)(side + Math.floor(event.getY()) - start.y - offset.y))), side + (getHeight() - points[2].y - 2* halfCorner ))),side + (getWidth() - points[1].x - 2* halfCorner ));
                points[2].y = points[0].y + side;
                points[3].y = points[0].y + side;
                points[3].x = points[0].x + side;
                points[1].x = points[0].x + side;
                start.y = points[2].y;
                invalidate();

            }else if (corner == 3){
                side = Math.min((Math.min((Math.min((Math.max(minimumSideLength, (int)(side + Math.floor(event.getX()) - start.x - offset.x))), side + (getWidth() - points[3].x - 2* halfCorner ))),side + (getHeight() - points[3].y - 2* halfCorner ))), Math.min((Math.min((Math.max(minimumSideLength, (int)(side + Math.floor(event.getY()) - start.y - offset.y))), side + (getHeight() - points[3].y - 2* halfCorner ))),side + (getWidth() - points[3].x - 2* halfCorner )));
                points[1].x = points[0].x + side;
                points[3].x = points[0].x + side;
                points[3].y = points[0].y + side;
                points[2].y = points[0].y + side;
                start.x = points[3].x;

                points[2].y = points[0].y + side;
                points[3].y = points[0].y + side;
                points[3].x = points[0].x + side;
                points[1].x = points[0].x + side;
                start.y = points[3].y;
                invalidate();

            }
            break;
        }
    }
    return true;
}

private int getCorner(float x, float y){
    int corner = 5;
    for (int i = 0; i < points.length; i++){
        float dx = x - points[i].x;
        float dy = y - points[i].y;
        int max = halfCorner * 2;
        if(dx <= max && dx >= 0 && dy <= max && dy >= 0){
            return i;
        }
    }
    return corner;
}

private Point getOffset(int left, int top, int corner){
    Point offset = new Point();
    if(corner == 5){
        offset.x = 0;
        offset.y = 0;
    }else{
        offset.x = left - points[corner].x;
        offset.y = top - points[corner].y;
    }
    return offset;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}

In your your layout xml:

1) You must specify a drawable move corner

2) You must specify a drawable for the resize corners

3) corner size defaults to 20px

4) minimumSide must be at least corner size--default value is 20px

5) You must include the namespace "http://schemas.android.com/apk/res-auto" in your root view (I used the alias, xlmn:app="http://schemas.android.com/apk/res-auto")

example xml layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="@color/primary"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


<ImageView
    android:id="@+id/crop_image"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginStart="10dp"
    android:layout_marginTop="10dp"
    android:layout_marginEnd="10dp"
    android:layout_marginBottom="10dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="1.0"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.0"
    app:srcCompat="@mipmap/ic_launcher" />

<your_package.IconCropView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/transparent"
    app:minimumSide="60dp"
    app:resizeCornerDrawable="@drawable/adjust_edge_circle"
    app:moveCornerDrawable="@drawable/move_box_circle"
    app:cornerColor="@color/turq"
    app:edgeColor="@color/colorPrimary"
    app:outsideCropColor="@color/transparent_50"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:cornerSize="20dp"/>


来源:https://stackoverflow.com/questions/8974088/how-to-create-a-resizable-rectangle-with-user-touch-events-on-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!