Get four coordinates of a rectangle, android canvas
Hi I want to create a custom view in that I am loading a bitmap that has a black border and on the right. There is a close button at the top
so when the user clicks that close button the image should be removed
To draw things like this, I did the 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 the image so that the RED-Close button also moves
I just want to get the RED-CIRCLE coordinate, or you can specify the coordinate of the second corner of the rectangle
I checked below link but was unable to succeed
Get the new position of the coordinate after rotation using the Matrix
source to share
Since you already have the rotation information, this is good. All you have to do is apply the rotation matrix to the original center point of your circle (as the original, I mean the point that is already translated and the rotation angle is 0 ° ... you have this information in your code). As you can see on the web page I gave you, the equation to get the 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: functions sin
and cos
take an angular parameter which is in RADians . Since I see you did the conversion yourself, 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: Explain what is wrong with your code and paste in a simple solution
Download the source HERE
There are two things in the code.
- The canvas rotates the object clockwise, which is the opposite of how mathematics defines rotation. (If you see it and know the guy who did it this way, slap him in the face.) To fix this, you must use a negative rotation angle.
- You rotate your point around the center of the view, but you are using the touch values in the Android coordinate system. These two are offset as shown in this image (I know I am terrible with images. Sorry)
Assuming that if you are converting a point from a 0 to 1 coordinate system, you should use the equations
x1 = x0 - view_size / 2
y1 = -y0 + view_size / 2
Here is the promised code. First create your own view called 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 a layout of the activity
<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 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.
I hope I didn't scare you off with math and these equations: /
source to share