1. 程式人生 > >Android 視窗Flags詳解

Android 視窗Flags詳解

轉自:https://www.jianshu.com/p/567ff949219a

Android 視窗Flags詳解

這裡主要探討Touchable,Focusable,OutsideTouchable,TouchModal這四個混合使用的效果。

public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;
public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;
public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;

private int computeFlags(int curFlags) {
    boolean mTouchable = true;
    if (!mTouchable) {
        curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
    } else {
        curFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
    }

    boolean mFocusable = false;
    if (!mFocusable) {
        curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    } else {
        curFlags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    }

    boolean mTouchModal = true;
    if (!mTouchModal) {
        curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
    } else {
        curFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
    }

    boolean mOutsideTouchable = true;
    if (mOutsideTouchable) {
        curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
    } else {
        curFlags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
    }

    return curFlags;
}
  1. Touchable (預設為true)

最簡單的Touchable,
為false,表示視窗不接受觸控事件;
為true,表示視窗接受觸控事件;

要視窗接收事件,必須為true。視窗不接受事件,意味著事件會透傳到下一個視窗。這裡的視窗事件是指DOWN-UP,視窗是指視窗自身範圍,視窗外的ACTION_OUTSIDE與此設定無關。

  1. OutsideTouchable(預設為false)

為false,表示對ACTION_OUTSIDE事件不感興趣。
為true,表示對ACTION_OUTSIDE事件感興趣,此時,如果新事件被另一個視窗消化,則會發送ACTION_OUTSIDE給該視窗。包括:

2.1. 如果視窗設定了Touchable為false。即使觸控事件在視窗內,由於不處理事件,導致事件透傳,被另一個視窗消化,此時該視窗也會收到ACTION_OUTSIDE。

2.2 觸控事件在視窗外面觸發,導致事件被另一個視窗消化,此時該視窗也會收到ACTION_OUTSIDE。

2.3 如果視窗設定了TouchModal和Focusable,導致視窗內外的事件被當前視窗截獲,由於不是被另一個視窗消化,所以即使設定了OutsideTouchable,也不會有ACTION_OUTSIDE。

  1. Focusable(預設為true)

為false,表示不會聚焦,所以不會有軟鍵盤。同時它的z-order可以在軟鍵盤之上,覆蓋軟鍵盤。如果你在不聚焦的情況下,還需要軟鍵盤,可以使用FLAG_ALT_FOCUSABLE_IM來修改。如果為false,會放棄TouchModal原來的值,強制設定TouchModal為false

為true,表示視窗可以聚焦。

Focusable生效,通常需要Touchable為true。如果視窗Touchable為false,視窗可以聚焦頂多可以彈出鍵盤,視窗自身不會收到Touch事件。

  1. TouchModal(預設為true)

TouchModal的設定,只有在Focusable為true時才有效,Focusable為false,會忽略TouchModal的值

為true,當視窗Focusable為true時,無論視窗內外,事件都被當前視窗接收。
為false,當視窗Focusable為true時,只有視窗內的事件被當前視窗接收。視窗外,OutsideTouchable的設定決定了是否有ACTION_OUTSIDE事件。

為true,當視窗Focusable為false時,設定不生效。
為false,當視窗Focusable為false時,設定不生效。

總結:

  1. 如何知道這些Flag的預設值?

FLAG_NOT_TOUCHABLE,意味著,預設是TOUCHABLE,必要時,才使用這個Flag關閉。
FLAG_NOT_FOCUSABLE,意味著,預設是FOCUSABLE,必要時,才使用這個Flag關閉。
FLAG_WATCH_OUTSIDE_TOUCH,意味著,預設是不關心,必要時,才使用這個Flag開啟。
FLAG_NOT_TOUCH_MODAL,意味著,預設是TOUCH_MODAL,必要時,才使用這個Flag關閉。

  1. 如何看Flag註釋裡的enable/disable:
/** Window flag: this window won't ever get key input focus, so the
 * user can not send key or other button events to it.  Those will
 * instead go to whatever focusable window is behind it.  This flag
 * will also enable {@link #FLAG_NOT_TOUCH_MODAL} whether or not that
 * is explicitly set.
 *
 * <p>Setting this flag also implies that the window will not need to
 * interact with
 * a soft input method, so it will be Z-ordered and positioned
 * independently of any active input method (typically this means it
 * gets Z-ordered on top of the input method, so it can use the full
 * screen for its content and cover the input method if needed.  You
 * can use {@link #FLAG_ALT_FOCUSABLE_IM} to modify this behavior. */
public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;

FLAG_NOT_FOCUSABLE的註釋是這樣的。This flag will also enable FLAG_NOT_TOUCH_MODAL whether or not that is explicitly set.

所謂的enable,就是使用這個Flag。直接翻譯就是,使用了FLAG_NOT_FOCUSABLE這個Flag,就會同時使用FLAG_NOT_TOUCH_MODAL這個flag。

使用FLAG_NOT_FOCUSABLE就是關閉FOCUSABLE,使用FLAG_NOT_TOUCH_MODAL就是關閉TOUCH_MODAL。簡述就是,關閉了FOCUSABLE,會同時關閉TOUCH_MODAL。

  1. 除了註釋中有說明,否則這些Flag都是單獨生效的。

比如,Touchable,當一個事件確實派發到視窗到時候,就看這個Flag,為true就是接受事件,為false就是不接受事件。你可以提前截獲不讓事件派發到視窗,但一旦派發到視窗,就是這個Flag來決定視窗是否接受事件。

比如,OutsideTouchable,你可以截獲事件不讓事件派發到另一個視窗,但一旦事件派發到另一個視窗,就是這個Flag決定當前視窗是否接受ACTION_OUTSIDE事件。

例項分析:

mWindowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.width = WindowManager.LayoutParams.WRAP_CONTENT;
p.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowManager.addView(view, p);

以下,視窗內外是指,觸點在視窗內/外。完整事件是指DOWN to UP事件流。ACTION_OUTSIDE事件只有一次。

  1. WindowManager.LayoutParams使用預設設定。
    意味著,都使用預設值:Touchable=true,OutsideTouchable=false,Focusable=true,TouchModal=true。
    結果是:視窗內外事件都被當前彈窗截獲。

  2. 上面引數把OutsideTouchable=true。即
    Touchable=true,OutsideTouchable=true,Focusable=true,TouchModal=true。
    結果是:同上,視窗內外事件都被當前彈窗截獲。沒有事件透傳到下一個視窗,所以內外不會有ACTION_OUTSIDE事件。

  3. Touchable=false,OutsideTouchable=false,Focusable=true,TouchModal=true。
    結果是:內外的事件都透傳,視窗收不到任何事件。(視窗內不接受事件,事件透傳。視窗外由OutsideTouchable=false控制,不接收ACTION_OUTSIDE事件,所以視窗收不到任何事件)

  4. 上面引數把OutsideTouchable=true。即
    Touchable=false,OutsideTouchable=true,Focusable=true,TouchModal=true。
    結果是:視窗內外都收到ACTION_OUTSIDE事件。(視窗內外事件都透傳到下一個視窗,相當於對於視窗而言,都是視窗外,所以點選視窗內外,視窗都能收到ACTION_OUTSIDE事件)

總結,Touchable和OutsideTouchable分別控制了視窗內外的事件

  1. Touchable=true,OutsideTouchable=true,Focusable=true,TouchModal=true。
    結果是:視窗內外事件都被當前彈窗截獲,是完整事件。內外都不會有ACTION_OUTSIDE事件。

  2. Touchable=true,OutsideTouchable=true,Focusable=true,TouchModal=false。
    結果是:視窗內有完整事件;視窗外有ACTION_OUTSIDE事件。如果將OutsideTouchable設為false,則視窗外沒有ACTION_OUTSIDE事件。

  3. Touchable=true,OutsideTouchable=true,Focusable=false,TouchModal=false/true。
    結果是:同上。視窗內有完整事件;視窗外有ACTION_OUTSIDE事件。如果將OutsideTouchable設為false,視窗外沒有ACTION_OUTSIDE事件。

總結,當且僅當Focusable為true,TouchModal為true情況下,視窗內外事件才被當前視窗截獲;否則,都是視窗內才有完整事件,視窗外才有ACTION_OUTSIDE事件

注意,當前視窗收到的完整事件和ACTION_OUTSIDE事件,都是先分發到視窗的DecorView,即WindowManager.addView(view, p);裡面的view例項。
當事件分發到View,就進入View的事件分發流程。



作者:southtrain
連結:https://www.jianshu.com/p/567ff949219a
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。