2018年9月14日金曜日

android:windowCloseOnTouchOutside を指定するとどうなるのか

android:windowCloseOnTouchOutside は API Level 11 で追加されたテーマ用の属性で、true を指定すると、Dialog 系の theme を指定した Activity でダイアログ(というか window)以外の領域をタップしたときにダイアログが閉じます(というか Activity が finish() します)。

この属性は Window に関するもので、Window では以下のフィールドとメソッドが関連します。

Window public abstract class Window { ... private boolean mCloseOnTouchOutside = false; private boolean mSetCloseOnTouchOutside = false; ... /** @hide */ public void setCloseOnTouchOutside(boolean close) { mCloseOnTouchOutside = close; mSetCloseOnTouchOutside = true; } /** @hide */ public void setCloseOnTouchOutsideIfNotSet(boolean close) { if (!mSetCloseOnTouchOutside) { mCloseOnTouchOutside = close; mSetCloseOnTouchOutside = true; } } ... /** @hide */ public boolean shouldCloseOnTouch(Context context, MotionEvent event) { final boolean isOutside = event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event) || event.getAction() == MotionEvent.ACTION_OUTSIDE; if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) { return true; } return false; } private boolean isOutOfBounds(Context context, MotionEvent event) { final int x = (int) event.getX(); final int y = (int) event.getY(); final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop(); final View decorView = getDecorView(); return (x < -slop) || (y < -slop) || (x > (decorView.getWidth()+slop)) || (y > (decorView.getHeight()+slop)); } } Window の shouldCloseOnTouch() では、mCloseOnTouchOutside が true、かつ peekDecorView が null ではない、かつ MotionEvent の action が MotionEvent.ACTION_OUTSIDE、もしくは MotionEvent.ACTION_DOWN でタップ位置が DecorView の外側の場合、true が返ります。

Window の setCloseOnTouchOutside() は hide になっていて通常のアプリからは呼べません。 ではコードでは指定できないのかというと、Activity の setFinishOnTouchOutside() から指定できます。

Activity public void setFinishOnTouchOutside(boolean finish) { mWindow.setCloseOnTouchOutside(finish); } Activity の onTouchEvent() で Window の shouldCloseOnTouch() を呼んでおり、これにより DecorView の外側をタップすると Activity が finish() します。

Activity public boolean onTouchEvent(MotionEvent event) { if (mWindow.shouldCloseOnTouch(this, event)) { finish(); return true; } return false; }

ちなみに android:windowCloseOnTouchOutside 属性の設定値は、PhoneWindow から Window.setCloseOnTouchOutsideIfNotSet() を呼ぶことで適用されています。



0 件のコメント:

コメントを投稿