Android 包含无法正常工作的圆弧方法
Android contains method on arcs not working properly
我有自定义视图控件,如下所示:
在我的 activity 中,我希望能够通过在绿色弧上拖动它来在屏幕上移动此视图(向左或向右无关紧要)。
还希望能够检测顶部黄色圆弧是否被点击,中间圆弧还是底部圆弧。
我无法检测水龙头在哪个区域。这是我在 activity:
中使用的代码
float dX, dY;
final MyCustomView myCustomView = (MyCustomView)findViewById(R.id.test);
final Boolean[] movable = {false};
myCustomView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
movable[0] = false;
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
int x = (int) event.getX();
int y = (int) event.getY();
if (myCustomView.leftArcRegion.contains(x,y) || myCustomView.rightArcRegion.contains(x,y)){
movable[0] = true;
} else if (myCustomView.topArcRegion.contains(x,y)){
//todo: do something if top arc area is selected
} else if (myCustomView.midRoundedBitmapRegion.contains(x,y)){
//todo: do something if mid bitmap area is selected
} else if (myCustomView.bottomArcRegion.contains(x,y)){
//todo: do something if bottom arc area is selected
}
break;
case MotionEvent.ACTION_MOVE:
if (movable[0]) {
view.animate()
.x(event.getRawX() + dX)
.y(event.getRawY() + dY)
.setDuration(0)
.start();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
default:
return false;
}
return true;
}
});
这些是来自我的自定义视图控件的 public 个字段:
public Region topArcRegion;
private Path topArc;
//topArc is my top arc path
RectF rectFTop = new RectF();
topArc.computeBounds(rectFTop, true);
topArcRegion = new Region();
topArcRegion.setPath(topArc, new Region((int) rectFTop.left, (int) rectFTop.top,
(int) rectFTop.right, (int) rectFTop.bottom));
但在使用 "contains" 方法检查时,这些区域看起来像是矩形,而不是弧形。因此,我没有得到预期的结果。
那么,我如何检测初始点击位置(顶部圆弧、底部圆弧、侧面圆弧或中间位图)以应用我的应用程序逻辑?
由于您只想检测弧段内的触摸,因此应该不会太复杂。
您的每个弧段都定义为两个同心圆之间以及起始角和结束角之间的 space。所以你真正想要做的就是做一个小三角来确定从圆心到你的触摸点的距离以及从圆心到你的触摸点的角度。
float x = touchevent.getX();
float y = touchevent.getY();
// Transform relative to arc centers
x -= circle1.x;
y -= circle1.y;
double dist = Math.sqrt(x*x + y*y);
double angle = Math.atan2(y,x) * 180 / Math.PI;
// Given an arc segment defined by circle1, circle2, angle1, angle2:
boolean touch = dist > circle1.radius && dist < circle2.radius &&
angle > angle1 && angle < angle2;
您可能需要根据 angle1 > angle2 或反之亦然进行一些尝试。如果任何角度有可能与零度角相交,那就有点棘手了。
Meta:为清楚起见,我使用 sqrt()
来计算距离,但您可以通过跳过 sqrt()
并改为比较距离²来优化此代码:
double dist2 = x*x + y*y;
if (dist2 > circle1.radius * circle1.radius &&
dist2 < circle2.radius * circle2.radius &&
...
再编辑一下:计算三角函数可能很昂贵;肯定比计算距离²贵很多。
为了优化,你应该在打扰三角之前检查圆半径的距离:
boolean touch = dist > circle1.radius && dist < circle2.radius;
if (touch) {
// This is only a *possible* touch, check the angles now
double angle = Math.atan2(y,x) * 180 / Math.PI;
touch = angle > angle1 && angle < angle2;
}
我有自定义视图控件,如下所示:
在我的 activity 中,我希望能够通过在绿色弧上拖动它来在屏幕上移动此视图(向左或向右无关紧要)。
还希望能够检测顶部黄色圆弧是否被点击,中间圆弧还是底部圆弧。
我无法检测水龙头在哪个区域。这是我在 activity:
中使用的代码float dX, dY;
final MyCustomView myCustomView = (MyCustomView)findViewById(R.id.test);
final Boolean[] movable = {false};
myCustomView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
movable[0] = false;
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
int x = (int) event.getX();
int y = (int) event.getY();
if (myCustomView.leftArcRegion.contains(x,y) || myCustomView.rightArcRegion.contains(x,y)){
movable[0] = true;
} else if (myCustomView.topArcRegion.contains(x,y)){
//todo: do something if top arc area is selected
} else if (myCustomView.midRoundedBitmapRegion.contains(x,y)){
//todo: do something if mid bitmap area is selected
} else if (myCustomView.bottomArcRegion.contains(x,y)){
//todo: do something if bottom arc area is selected
}
break;
case MotionEvent.ACTION_MOVE:
if (movable[0]) {
view.animate()
.x(event.getRawX() + dX)
.y(event.getRawY() + dY)
.setDuration(0)
.start();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
default:
return false;
}
return true;
}
});
这些是来自我的自定义视图控件的 public 个字段:
public Region topArcRegion;
private Path topArc;
//topArc is my top arc path
RectF rectFTop = new RectF();
topArc.computeBounds(rectFTop, true);
topArcRegion = new Region();
topArcRegion.setPath(topArc, new Region((int) rectFTop.left, (int) rectFTop.top,
(int) rectFTop.right, (int) rectFTop.bottom));
但在使用 "contains" 方法检查时,这些区域看起来像是矩形,而不是弧形。因此,我没有得到预期的结果。
那么,我如何检测初始点击位置(顶部圆弧、底部圆弧、侧面圆弧或中间位图)以应用我的应用程序逻辑?
由于您只想检测弧段内的触摸,因此应该不会太复杂。
您的每个弧段都定义为两个同心圆之间以及起始角和结束角之间的 space。所以你真正想要做的就是做一个小三角来确定从圆心到你的触摸点的距离以及从圆心到你的触摸点的角度。
float x = touchevent.getX();
float y = touchevent.getY();
// Transform relative to arc centers
x -= circle1.x;
y -= circle1.y;
double dist = Math.sqrt(x*x + y*y);
double angle = Math.atan2(y,x) * 180 / Math.PI;
// Given an arc segment defined by circle1, circle2, angle1, angle2:
boolean touch = dist > circle1.radius && dist < circle2.radius &&
angle > angle1 && angle < angle2;
您可能需要根据 angle1 > angle2 或反之亦然进行一些尝试。如果任何角度有可能与零度角相交,那就有点棘手了。
Meta:为清楚起见,我使用 sqrt()
来计算距离,但您可以通过跳过 sqrt()
并改为比较距离²来优化此代码:
double dist2 = x*x + y*y;
if (dist2 > circle1.radius * circle1.radius &&
dist2 < circle2.radius * circle2.radius &&
...
再编辑一下:计算三角函数可能很昂贵;肯定比计算距离²贵很多。
为了优化,你应该在打扰三角之前检查圆半径的距离:
boolean touch = dist > circle1.radius && dist < circle2.radius;
if (touch) {
// This is only a *possible* touch, check the angles now
double angle = Math.atan2(y,x) * 180 / Math.PI;
touch = angle > angle1 && angle < angle2;
}