我正在尝试让我的应用显示一个圆圈,用户可以通过触摸和拖动该圆圈来将其旋转到一定程度。说圆的最大可旋转角度为60度。
我扩展了View类以创建自定义View。 我已经使用drawCircle()方法在画布上绘制了一个圆圈。 要旋转它,我知道我必须做一个canvas.rotate(),但是我不确定将中心固定为什么。 如何测量这个角度? 我要旋转什么?
public class DrawingView extends View {
Paint paint;
Canvas canvas;
int originalX = 100;
int originalY = 50;
int radius = 75;
// ... some code ...
// paint, canvas initialised in constructors
@Override
protected void onDraw(Canvas canvas){
canvas.drawCircle(originalX, originalY, radius, paint);
// some more code ...
}
@Override
public boolean onTouchEvent(MotionEvent event){
int x = (int)event.getX();
int y = (int)event.getY();
case MotionEvent.ACTION_DOWN:
// Detect if touch in my circle
break;
case MotionEvent.ACTION_MOVE:
double tx = x - originalX;
double ty = y - originalY;
double tlength = Math.sqrt(tx*tx + ty*ty);
double angle = Math.acos(radius/tlength);
double degrees = (angle * 180)/Math.PI;
System.out.println("Degrees " + (angle*180)/Math.PI);
break;
}
}
我显示的代码似乎给我的值不正确。 就像,如果我一直触摸并拖动到屏幕末端,它会为我提供大约70度的值。我不认为我在计算正确的角度,但是我也不知道该怎么做。
我遵循所选答案的找到角度的方法。 然后,我在绘制圆之前使用了canvas.rotate()。
答案 0 :(得分:0)
我不确定您是否知道该如何正确运行。但是那里的几何形状肯定很奇怪。 tlength
是从圆心到指尖的半径,您正在对其进行acos。但是,那是错误的。 acos是哪个值赋予您该值的余弦。从理论上讲,这将是不同角度的余弦波。这可能很有趣,但听起来并不像您想要的。听起来您想呼叫距0中心的距离以-60度旋转,半径以0度旋转,半径为2 *半径以+60度旋转。所以就那样做。您可以线性插入距离(60°*(距离/半径))-60°或其他值。
尽管实际上从UI的角度来看,最好还是让用户通过将手指移动到该角度来更改圆上的实际角度。然后将这些值放在atan2()中,并在超出允许范围时对其进行限制。
有很多方法可以做到这一点。您可以基于手指按压来计算角度。因此,您可以使用已知点,例如中心点和当前极坐标。因此,如果您当前的角度为r,则另一端的点为cos(x)*r
,sin(y)*r
如果您拥有该端点的x和y,则距中心的距离为sqrt(dx*dx + dy*dy)
,它的角度为atan2(dy, dx)
。因此,您可以通过一点点尝试从极地转到笛卡尔式。如果您想将中心和半径之差称为角度,则可以对其进行线性插值。
这里是一个快速渲染视图的视图。还有其他UI选择可以替代,例如,不进行实际角度调整,而是进行角度更改,因此您可以像使用旋钮一样移动圆。
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class RotateCircleView extends View {
static final double TAU = Math.PI * 2;
Paint paint;
double angle = 0;
double radius = 0;
double ix = Double.NaN, iy = Double.NaN;
public RotateCircleView(Context context) {
super(context);
init();
}
public RotateCircleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public RotateCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init() {
paint = new Paint();
paint.setAlpha(100);
paint.setStrokeWidth(20);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
ix = w / 2.0;
iy = h / 2.0;
radius = Math.min(ix, iy) / 2.0;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
if (Double.isNaN(ix)) return;
super.onDraw(canvas);
canvas.save();
canvas.rotate((float) ((TAU * -angle) / 360.0), (float) ix, (float) iy);
canvas.drawCircle((float) ix, (float) iy, (float) radius, paint);
double limit = TAU / 6.0;
if (angle > limit) angle = limit;
if (angle < -limit) angle = -limit;
double tx = ix + Math.cos(angle) * radius;
double ty = iy + Math.sin(angle) * radius;
canvas.drawLine((float) ix, (float) iy, (float) tx, (float) ty, paint);
canvas.restore();
}
public boolean onTouchEvent(MotionEvent event) {
double mx = event.getX();
double my = event.getY();
double dx = mx - ix;
double dy = my - iy;
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
//radius = Math.sqrt(dx * dx + dy * dy);
angle = Math.atan2(dy, dx);
invalidate();
break;
}
return true;
}
}