Siddhpura Amit
Siddhpura Amit

Reputation: 15078

get four co-ordinates of rectangle, canvas android

Hi I want to make an custom view in that I am loading bitmap which has black border and right|top side there is close button

so when ever user press that close button that image should be removed

For drawing this kind of things I have done following code

public void draw(Canvas canvas) {
        canvas.save();

        float dx = (mMaxX + mMinX) / 2;
        float dy = (mMaxY + mMinY) / 2;

        mDrawable.setBounds((int) mMinX, (int) mMinY, (int) mMaxX, (int) mMaxY);

        canvas.translate(dx, dy);
        float rotation = mAngle * 180.0f / (float) Math.PI;
        canvas.rotate(rotation);
        canvas.translate(-dx, -dy);

        canvas.drawRect(mMinX - 10, mMinY - 10, mMaxX + 10, mMaxY + 10,
                paintBoundry);

        mDrawable.draw(canvas);
        canvas.drawCircle(mMaxX, mMinY, (mMaxX - mMinX) / 8, paintBoundry);

        canvas.restore();
    }

Now my question is when I rotate image that RED-Close button will also moved

I want to just get RED-CIRCLE's co-ordinate, or you can say rectangle's second corner co-ordinate

I have checked below link but could not get success

Get new position of coordinate after rotation with Matrix

Upvotes: 1

Views: 3355

Answers (1)

Bojan Kseneman
Bojan Kseneman

Reputation: 15668

Since you already have the information about the rotation, that is good. All you need to do now is apply rotation matrix to the original center point of your circle (as original, I am referring to the point that is already translated and it's rotation angle is 0°... you have that information in your code). As you can see from the webpage I provided you, the equation to get current x and y values would be:

x_new = x_original * Math.cos(mAngle) - y_original * Math.sin(mAngle);
y_new = x_original * Math.sin(mAngle) + y_original * Math.cos(mAngle);

Note: sin and cos functions take an angular parameter that is in RADians. As I can see you were doing some converting on your own, you can use Math.toDegrees(double radians) and Math.toRadians(double degrees)

You can use the same principle for the other three points if you need them.

EDIT: Explaining what is wrong with your code and pasting a simple solution

Download the source HERE

There are two things wrong with your code.

  • Canvas rotates object in the clockwise direction, which is the opposite of how math defines rotation. (If you see this and know the guy that made it this way, give him a slap to the face in my name). To fix this, you have to use a negative angle of rotation.
  • You are rotating your point arround the center of the view, yet you are using touch values in Android's coordinate system. The two are offset like this image shows (I know, I'm terrible with images. Sorry)

enter image description here

Meaning that if you convert a point from the coordinate system 0 to 1 you would use equations

x1 = x0 - view_size / 2
y1 = -y0 + view_size / 2

Here is the promised code. First create a custom view named RotationSquare

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;


public class RotationSquare extends View {


    private int squareBorderColor = 0xFF000000; // black
    private int innerCircleColor = 0xFFFF0000; // red
    private int outerCircleColor = 0xFF000000; // black

    private Paint squarePaint, innerCirclePaint, outerCirclePaint;
    private RectF squareRect;

    private int measuredDimenson;

    private float rotation = 0;

    // Circle parameters that should be remembered
    private float innerRadius, outerRadius, centerX, centerY;
    boolean touchedInsideCircle;


    public RotationSquare(Context context) {
        super(context);
        init(context, null, 0);
    }

    public RotationSquare(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public RotationSquare(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attributeSet, int defStyle) {


        squareRect = new RectF();

        squarePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        squarePaint.setColor(squareBorderColor);
        squarePaint.setStyle(Paint.Style.STROKE);

        innerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        innerCirclePaint.setColor(innerCircleColor);
        innerCirclePaint.setStyle(Paint.Style.FILL);

        outerCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        outerCirclePaint.setColor(outerCircleColor);
        outerCirclePaint.setStyle(Paint.Style.FILL);


    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int measuredHeight = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        int measuredWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);

        // Make a square
        measuredDimenson = Math.min(measuredHeight, measuredWidth);


        squareRect.set(0.25f * measuredDimenson, 0.25f * measuredDimenson, 0.85f * measuredDimenson, 0.85f * measuredDimenson);
        squarePaint.setStrokeWidth(0.025f * measuredDimenson);

        centerX = squareRect.right;
        centerY = squareRect.top;
        innerRadius = 0.07f * measuredDimenson;
        outerRadius = 0.08f * measuredDimenson;


        setMeasuredDimension((int) (measuredDimenson * 1f), (int) (measuredDimenson * 1f));
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (measuredDimenson <= 0) {
            return;
        }

        canvas.save(Canvas.MATRIX_SAVE_FLAG);

        canvas.rotate(rotation, measuredDimenson / 2, measuredDimenson / 2);

        canvas.drawCircle(centerX, centerY, outerRadius, outerCirclePaint);
        canvas.drawCircle(centerX, centerY, innerRadius, innerCirclePaint);
        canvas.drawRect(squareRect, squarePaint);
        canvas.restore();

    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX(0);
        float y = event.getY(0);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchedInsideCircle = isTouchInsideCircle(x, y);
                return true;
            case MotionEvent.ACTION_MOVE:
                return true;
            case MotionEvent.ACTION_UP:
                if (touchedInsideCircle && isTouchInsideCircle(x, y)) {
                    Toast.makeText(getContext(), "Clicked the circle", Toast.LENGTH_SHORT).show();
                }
                return true;
            default:
                return super.onTouchEvent(event);
        }

    }

    public void setRotation(int rotation) {
        this.rotation = rotation;
        invalidate();
    }


    private boolean isTouchInsideCircle(float x, float y) {
        double angleRad = Math.toRadians(rotation);

        // Convert to the centered coordinate system
        double centerXConverted = centerX - measuredDimenson / 2;
        double centerYConverted = -centerY + measuredDimenson / 2;

        // Use the negative angle
        double currentCenterX = centerXConverted * Math.cos(-angleRad) - centerYConverted * Math.sin(-angleRad);
        double currentCenterY = centerXConverted * Math.sin(-angleRad) + centerYConverted * Math.cos(-angleRad);

        // Convert to the centered coordinate system
        x = x - measuredDimenson / 2;
        y = -y + measuredDimenson / 2;

        double squareRadius = outerRadius * outerRadius;
        double squaredXDistance = (x - currentCenterX) * (x - currentCenterX);
        double squaredYDistance = (y - currentCenterY) * (y - currentCenterY);

        return (squaredXDistance + squaredYDistance) < squareRadius;
    }
}

Make activity layout

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <some.package.that.you.must.change.RotationSquare
        android:id="@+id/rotatingSquare"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"/>

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:max="360"/>

    <TextView
        android:id="@+id/tvRotation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/seekBar"
        android:gravity="center_horizontal"
        android:text="Rotation: 0°"
        android:textSize="15sp"/>

</RelativeLayout>

And the Activity

public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {

    private SeekBar seekBar;
    private RotationSquare rotationSquare;
    private TextView tvRotation;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        rotationSquare = (RotationSquare) findViewById(R.id.rotatingSquare);
        seekBar = (SeekBar) findViewById(R.id.seekBar);
        seekBar.setOnSeekBarChangeListener(this);

        tvRotation = (TextView) findViewById(R.id.tvRotation);
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
        rotationSquare.setRotation(i);
        tvRotation.setText("Rotation: " + i + "°");
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }
}

Here is the final result.

enter image description here

Hope I didn't scare you off with the math and these equations :/

Upvotes: 10

Related Questions