2018年9月14日金曜日

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

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

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

Window
  1. public abstract class Window {  
  2.     ...  
  3.   
  4.     private boolean mCloseOnTouchOutside = false;  
  5.     private boolean mSetCloseOnTouchOutside = false;  
  6.   
  7.     ...  
  8.   
  9.     /** @hide */  
  10.     public void setCloseOnTouchOutside(boolean close) {  
  11.         mCloseOnTouchOutside = close;  
  12.         mSetCloseOnTouchOutside = true;  
  13.     }  
  14.   
  15.     /** @hide */  
  16.     public void setCloseOnTouchOutsideIfNotSet(boolean close) {  
  17.         if (!mSetCloseOnTouchOutside) {  
  18.             mCloseOnTouchOutside = close;  
  19.             mSetCloseOnTouchOutside = true;  
  20.         }  
  21.     }  
  22.   
  23.     ...  
  24.   
  25.     /** @hide */  
  26.     public boolean shouldCloseOnTouch(Context context, MotionEvent event) {  
  27.         final boolean isOutside =  
  28.                 event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)  
  29.                 || event.getAction() == MotionEvent.ACTION_OUTSIDE;  
  30.         if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {  
  31.             return true;  
  32.         }  
  33.         return false;  
  34.     }  
  35.   
  36.     private boolean isOutOfBounds(Context context, MotionEvent event) {  
  37.         final int x = (int) event.getX();  
  38.         final int y = (int) event.getY();  
  39.         final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();  
  40.         final View decorView = getDecorView();  
  41.         return (x < -slop) || (y < -slop)  
  42.                 || (x > (decorView.getWidth()+slop))  
  43.                 || (y > (decorView.getHeight()+slop));  
  44.     }  
  45.   
  46. }  
Window の shouldCloseOnTouch() では、mCloseOnTouchOutside が true、かつ peekDecorView が null ではない、かつ MotionEvent の action が MotionEvent.ACTION_OUTSIDE、もしくは MotionEvent.ACTION_DOWN でタップ位置が DecorView の外側の場合、true が返ります。

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

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

Activity
  1. public boolean onTouchEvent(MotionEvent event) {  
  2.     if (mWindow.shouldCloseOnTouch(this, event)) {  
  3.         finish();  
  4.         return true;  
  5.     }  
  6.   
  7.     return false;  
  8. }  


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



0 件のコメント:

コメントを投稿