web-dev-qa-db-ja.com

Androidでビュー外のタッチを処理する方法は?

Android用のGmailアプリケーションで使用される "Undo Bar" の実装を見つけました。 「UndoBar」は基本的に、レイアウトの上に表示されるビューです。

残念ながら、それは完全ではありません-バーの外側の画面に触れてバーを閉じる機能はありません。

バーの非表示を処理するためにFrameLayoutをオーバーライドするonInterceptTouchEventを実装しましたが、アクションバーに触れても何も起こりません。

アクションバーからそのようなイベントを処理する方法はありますか?

以下に「UndoBar」が表示された画像があります。赤い点で表されたアクションバーのタッチを処理するために達成したいこと。

enter image description here

11
pixel

アクティビティのdispatchTouchEventをオーバーライドしてみてください。

dispatchTouchEvent(MotionEvent event){

     int x= event.getRawX();
     int y= event.getRawY();


         if(/*check bounds of your view*/){
          // set your views visiblity to gone or what you want. 
         }
      //for prevent consuming the event.
      return super().dispatchTouchEvent(event);    
}

更新

dispatchTouchEvent(MotionEvent event){

     int x= event.getRawX();
     int y= event.getRawY();

      return super().dispatchTouchEvent(event)||[YourView].onTouch(event);    
}
6
Ercan

アクションバーを含む画面全体のタッチイベントをキャッチするには、ウィンドウにビューを追加します。

View overlayView = new View(this);
WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.gravity = Gravity.TOP;
p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
p.token = overlayView.getWindowToken();
overlayView.setOnTouchListener(new OnTouchListener() {
    public boolean onTouch(View view, MotionEvent event) {
        // Get the action bar
        int actionBarHeight = actionBar.getHeight();
        if ((event.getAction() == MotionEvent.ACTION_DOWN)
            && (event.getRawY() < actionBarHeight)) {
            // Touch inside the actionBar so let's consume it
            // Do something
            return true;
        }
        return false;
    }
});
WindowManager mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
mWindowManager.addView(overlayView, p);

お役に立てれば。

4

アクティビティのdispatchTouchEvent(ev:MotionEvent)メソッドをオーバーライドする必要があります


不定代名詞を却下するこの例を見てください

class MainActivity : AppCompatActivity() {

private var snackbar: Snackbar? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)

    // show indefinite snackbar
    snackbar = Snackbar.make(coordinator_layout, "Hello world!", Snackbar.LENGTH_INDEFINITE).apply {
        show()
    }
}

/**
 * On each touch event:
 * Check is [snackbar] present and displayed
 * and dismiss it if user touched anywhere outside it's bounds
 */
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
    // dismiss shown snackbar if user tapped anywhere outside snackbar
    snackbar?.takeIf { it.isShown }?.run {
        val touchPoint = Point(Math.round(ev.rawX), Math.round(ev.rawY))
        if (!isPointInsideViewBounds(view, touchPoint)) {
            dismiss()
            snackbar = null // set snackbar to null to prevent this block being executed twice
        }
    }

    // call super
    return super.dispatchTouchEvent(ev)
}

/**
 * Defines bounds of displayed view and check is it contains [Point]
 * @param view View to define bounds
 * @param point Point to check inside bounds
 * @return `true` if view bounds contains point, `false` - otherwise
 */
private fun isPointInsideViewBounds(view: View, point: Point): Boolean = Rect().run {
    // get view rectangle
    view.getDrawingRect(this)

    // apply offset
    IntArray(2).also { locationOnScreen ->
        view.getLocationOnScreen(locationOnScreen)
        offset(locationOnScreen[0], locationOnScreen[1])
    }

    // check is rectangle contains point
    contains(point.x, point.y)
}
}        

または完全にチェックしてください 要点

1
Andrey Busik

FrameLayoutでonTouchEventを試しましたか?

私はそれを試していませんが、そのレイアウトでMotionEventの場合はACTION_OUTSIDEアクションが発生するはずです。

次のことを試してください。

public boolean onTouchEvent(MotionEvent event)  
{  

       if(event.getAction() == MotionEvent.ACTION_OUTSIDE){  

             undoBar.setVisibility(View.GONE);
       }  
       return true;  
}  
0
Puru Pawar

私はここで見つけた答えのどれにも満足していませんでした。私の解決策は、次のようにタッチリスナーを親ビューにアタッチすることです。

((View)getParent()).setOnTouchListener((v, event) -> {
    return true;
});
0
adriendenat
params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSPARENT);

実際に必要なのは:

WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

ドキュメントによると:

ウィンドウフラグ:FLAG_NOT_TOUCH_MODALを設定している場合、このフラグを設定して、ウィンドウの外側で発生するタッチに対してアクションMotionEvent.ACTION_OUTSIDEを持つ単一の特別なMotionEventを受信できます。

0

2つの可能な解決策があります。

コメントで述べたように、ListViewにスクロールリスナーを実装するか、わずかなスクロールでhideUndoBar(true)と呼ばれる単純なものを実装できます。

OR

UndoBarControllerを変更できます。元に戻すバーは、コンストラクターとビューのshowメソッドsetFocusでViewリスナーをOnFocusChangeに叩きつけるだけのViewであることがわかります。

OnFocusChangeで、ビューがフォーカスを失ったかどうかを確認し、hideUndoBar(true)を呼び出します。

更新

ここで要点を作成しました https://Gist.github.com/atgheb/5551961UndoBarControllerを変更して、フォーカスを失ったときに非表示になる機能を追加する方法を示します。

私はそれをテストしていませんが、なぜそれが機能しないのかわかりません。

0
Ali