1

我有自定义视图控件,如下所示:

在此处输入图像描述

在我的活动中,我希望能够通过在绿色弧上拖动这个视图来在屏幕上移动这个视图(左或右无关紧要)。

还希望能够检测是否点击了顶部的黄色弧线、中间的圆弧或底部的弧线。

我无法检测水龙头在哪个区域。这是我在活动中使用的代码:

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 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));

但是在使用这种“包含”方法进行检查时,看起来它对这些区域使用了矩形,而不是弧形。正因为如此,我没有得到预期的结果。

那么,为了应用我的应用程序逻辑,我如何检测初始点击(顶部弧、底部弧、侧弧或中间位图)在哪里?

4

1 回答 1

1

由于您只想检测弧段内的触摸,因此它不应该太复杂。

每个弧段都定义为两个同心圆之间以及起始角和结束角之间的空间。所以你真正想做的就是做一个小三角来确定从圆心到你的接触点的距离以及从圆心到你的接触点的角度。

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;

您可能需要根据角度 1 > 角度 2 或反之亦然进行一些调整。如果有任何角度有可能与零度角相交,那就有点棘手了。


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;
}
于 2016-02-16T15:46:02.270 回答