Android用のGmailアプリケーションで使用される "Undo Bar" の実装を見つけました。 「UndoBar」は基本的に、レイアウトの上に表示されるビューです。
残念ながら、それは完全ではありません-バーの外側の画面に触れてバーを閉じる機能はありません。
バーの非表示を処理するためにFrameLayout
をオーバーライドするonInterceptTouchEvent
を実装しましたが、アクションバーに触れても何も起こりません。
アクションバーからそのようなイベントを処理する方法はありますか?
以下に「UndoBar」が表示された画像があります。赤い点で表されたアクションバーのタッチを処理するために達成したいこと。
アクティビティの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);
}
アクションバーを含む画面全体のタッチイベントをキャッチするには、ウィンドウにビューを追加します。
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);
お役に立てれば。
アクティビティの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)
}
}
または完全にチェックしてください 要点
FrameLayoutでonTouchEvent
を試しましたか?
私はそれを試していませんが、そのレイアウトでMotionEvent
の場合はACTION_OUTSIDE
アクションが発生するはずです。
次のことを試してください。
public boolean onTouchEvent(MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_OUTSIDE){
undoBar.setVisibility(View.GONE);
}
return true;
}
私はここで見つけた答えのどれにも満足していませんでした。私の解決策は、次のようにタッチリスナーを親ビューにアタッチすることです。
((View)getParent()).setOnTouchListener((v, event) -> {
return true;
});
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を受信できます。
2つの可能な解決策があります。
コメントで述べたように、ListView
にスクロールリスナーを実装するか、わずかなスクロールでhideUndoBar(true)
と呼ばれる単純なものを実装できます。
OR
UndoBarController
を変更できます。元に戻すバーは、コンストラクターとビューのshowメソッドsetFocusでView
リスナーをOnFocusChange
に叩きつけるだけのView
であることがわかります。
OnFocusChange
で、ビューがフォーカスを失ったかどうかを確認し、hideUndoBar(true)
を呼び出します。
ここで要点を作成しました https://Gist.github.com/atgheb/5551961UndoBarController
を変更して、フォーカスを失ったときに非表示になる機能を追加する方法を示します。
私はそれをテストしていませんが、なぜそれが機能しないのかわかりません。