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

円弧セグメント内のタッチを検出するだけなので、それほど複雑ではありません。

各円弧セグメントは、2 つの同心円の間、および開始角度と終了角度の間のスペースとして定義されます。したがって、実際にやりたいことは、円の中心からタッチ ポイントまでの距離と、中心からタッチ ポイントまでの角度を決定するための小さな三角関数を実行することだけです。

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 またはその逆かどうかに応じて、おそらく少しいじる必要があります。いずれかの角度が 0 度の角度を横切る可能性がある場合は、少し複雑になります。


メタ: わかりやすくするために、以前sqrt()は距離を計算していましたが、代わりに距離² をスキップしsqrt()て比較することで、このコードを最適化できます。

double dist2 = x*x + y*y;
if (dist2 > circle1.radius * circle1.radius &&
    dist2 < circle2.radius * circle2.radius &&
    ...

もう 1 つ編集: 三角関数の計算はコストがかかる可能性があります。確かに、距離を計算するよりもはるかに高価です²。

最適化のために、三角関数を使用する前に、円の半径に対する距離を確認する必要があります。

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 に答える