Поскольку у вас уже есть информация о ротации, это хорошо. Все, что вам нужно сделать сейчас, это применить матрицу вращения к исходной центральной точке вашего круга (как оригинал, я имею в виду точку, которая уже переведена, и ее угол поворота равен 0 ° ... у вас есть эта информация в вашем коде). Как вы можете видеть на веб-странице, которую я вам предоставил, уравнение для получения текущих значений x и y будет выглядеть так:
x_new = x_original * Math.cos(mAngle) - y_original * Math.sin(mAngle);
y_new = x_original * Math.sin(mAngle) + y_original * Math.cos(mAngle);
Примечание. Функции sin
и cos
принимают угловой параметр, который находится в RADians. Как я вижу, вы выполняли некоторые преобразования самостоятельно, вы можете использовать Math.toDegrees(двойные радианы) и Math.toRadians(двойные градусы)
Вы можете использовать тот же принцип для остальных трех точек, если они вам нужны.
РЕДАКТИРОВАТЬ: объяснить, что не так с вашим кодом, и вставить простое решение
Загрузите исходный код ЗДЕСЬ
В вашем коде неправильные две вещи.
- Холст вращает объект по часовой стрелке, что противоположно тому, как математика определяет вращение. (Если вы видите это и знаете парня, который сделал это таким образом, дайте ему пощечину от моего имени). Чтобы исправить это, вы должны использовать отрицательный угол поворота.
- Вы вращаете свою точку вокруг центра изображения, но используете сенсорные значения в системе координат Android. Эти два смещены, как показано на этом изображении (я знаю, я ужасен с изображениями. Извините)
Это означает, что если вы преобразуете точку из системы координат 0 в 1, вы будете использовать уравнения
x1 = x0 - размер_просмотра / 2
y1 = -y0 + размер_просмотра / 2
Вот обещанный код. Сначала создайте собственное представление с именем 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;
}
}
Сделать макет активности
<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>
И 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) {
}
}
Вот окончательный результат.
Надеюсь, я не напугал вас математикой и этими уравнениями :/
01.05.2015