2

注:この質問の元のタイトルを変更しました。最初は「一度に複数の GestureDetector インスタンスを使用する方法は?」というタイトルでしたが、問題は複数の検出器ではなく、ビューの階層に関係していることがわかりました。残りの質問はそのままにしておきます。何も削除しないことにしました。ただ読んでください。廃止された部品を捨てる必要があるかどうかアドバイスをください。そうする準備はできていますが、そうすべきかどうかはわかりません。

別のビュー (コンテナー) 内に 1 つのビュー (ボタン) があり、2 つのことを行う必要があります。1) ユーザーがビューをスクロール (タップして移動) すると、コンテナー全体がスクロールされます。2) ユーザーがボタンをクリックすると、ボタンのクリック ハンドラが呼び出されます。

私のコンテナーは単純なビューではありません。両方向にスクロールできます (詳細はこちら)。そのため、スクロール イベントを検出してコンテナーをスクロールする GestureDetector をセットアップしました。323goのアドバイスに感謝します。このディテクタは、メイン コンテナ ビューにアタッチされた OnTouchListener の onTouch メソッドから呼び出されます。

私の問題はボタンです。

onClick ハンドラーを設定すると、すべてのモーション イベントが完全にシンクされるように見えるため、ボタンでスクロールを開始すると、ビューをスクロールできません。

OK、ボタンに別の GestureDetector を設定して、クリックとスクロールを処理できるようにすることはできますか?

残念ながら、それはうまくいきません。ボタンの onScroll ハンドラーは、距離 X および距離 Y パラメーターでクレイジーな値を受け取ります。コンテナ ビューとボタンの両方に独自のスクロール カウンターがあるように見えるため、ビューをスクロールすると、ボタンのオフセットが蓄積されます。

だから私の質問は: GestureDetector クラスを使用する意図された方法は何ですか? ビューの階層で複数の検出器を使用できますか? また、ある検出器から別の検出器に処理を委任することは可能ですか? つまり、子検出器が false を返した場合、親検出器はイベントを処理できます。

追加:実際、このような単純な「親から子へ」のソリューションに満足しています: 階層の最上位ビューにアタッチされた唯一の GestureDetector があり、すべてのイベントを受け取り、それらの処理方法を決定します。スクロールを検出 — ビュー全体を単にスクロールし、誰にも通知しませんが、クリックを検出した場合 — クリック位置の下にある子ビューを見つけて、クリックをそこに転送します。しかし、それは真の Androidのやり方でしょうか? そして、それは可能ですか?

詳細な調査:以下のコメントに記載されているアドバイスに従い、子のジェスチャー検出器が onScroll メソッドから false を返すようにしました。子ハンドラーが false を返した場合、親ハンドラーはまったく呼び出されません。

しかし、子ハンドラーが親ビューをスクロールしようとしない場合、正しい値を受け取ることに気付きました。

これが私のハンドラのコードです:

private class ParentHandler extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        Log.d("UI", "Parent Scroll X: " + String.valueOf(distanceX) + " Y: " + String.valueOf(distanceY));
        scrollBy((int)distanceX, (int)distanceY);
        return true;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {return true;}

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {return true;}

    @Override
    public boolean onDown(MotionEvent e) {return true;}
};

private class ChildHandler extends GestureDetector.SimpleOnGestureListener {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        Log.d("UI", "Child Scroll X: " + String.valueOf(distanceX) + " Y: " + String.valueOf(distanceY));
        // The line below drives the handler crazy!
        // But if I remove it, the view will not be scrolled at all
        // because the parent handler is not called
        // even if this handler returned false.
        scrollBy((int)distanceX, (int)distanceY);
        return false;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {return true;}

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {return true;}

    @Override
    public boolean onDown(MotionEvent e) {return true;}
};

2 番目のハンドラのコメントに注意してください。

クレイジーコールが削除された場合、ログに記録されるものは次のとおりです。

Parent Scroll X: -1.0 Y: 2.0
Parent Scroll X: 0.0 Y: 2.0
Parent Scroll X: -1.0 Y: 1.0
Parent Scroll X: -1.0 Y: 2.0
Parent Scroll X: 0.0 Y: 2.0
...
Child Scroll X: -1.0 Y: 2.0
Child Scroll X: 0.0 Y: 2.0
Child Scroll X: -1.0 Y: 1.0
Child Scroll X: -1.0 Y: 2.0
Child Scroll X: 0.0 Y: 2.0

しかし、子ハンドラーでビューを移動しようとするとすぐに、値は次のようになります。

Child Scroll X: 13.0 Y: 22.0
Child Scroll X: -11.0 Y: -15.9999695
Child Scroll X: 11.0 Y: 17.00003
Child Scroll X: -10.0 Y: -16.00003
Child Scroll X: 11.0 Y: 19.99997

値が交互になっていることに注意してください。13 の後に -11 が続き、11 が次に続き、次に -10 というように続きます。したがって、平均ドリフトは問題ないように見えますが、これらの値を scrollBy 呼び出しに渡すだけでは、スクロールしている間ずっとビューがジッターしてあちこちジャンプします。

明らかに、子ビューに対して起動された onScroll 内で scrollBy を呼び出すと、いくつかの問題があります。親ハンドラから同じ呼び出しを行う場合は問題ありません。

何が間違っている可能性がありますか?

追加:親にアタッチされている GestureDetector を削除しましたが、動作は同じです。

というわけで、問題は実際には次のようになります。GestureDetector によってスクロールが検出されたときに、ビュー全体をスクロールする必要があります。ビューをスクロールするには、scrollBy を呼び出します。これは、元の onTouch イベントが親ビューに対して発生した場合は機能しますが、子ビューに対して onTouch が発生した場合は機能しません。後者の場合、親ビューをスクロールしようとすると、GestureHandler が受け取る値に影響します。

したがって、これは機能します:

parent.onTouch ->detector.onTouchEvent ->parent.scrollBy

これはしません:

child.onTouch ->detector.onTouchEvent ->parent.scrollBy

検出器は同じです。

4

1 に答える 1

2

複合設計パターンを使用して、イベントを他のリスナーに転送するリスナーを作成できます。

これらの投稿を見てください:

Androidのビューに複数のリスナーをアタッチしますか?

1 つのイベントに対して複数のリスナーを設定するにはどうすればよいですか?

于 2013-01-04T08:14:51.820 に答える