Reputation: 15078
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
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.
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.
Hope I didn't scare you off with the math and these equations :/
Upvotes: 10