WindowManager的一些屬性

WindowManager(窗口管理服務),它是顯示View的最底層,Toast,Activity,Dialog的底層都有用到了這個WindowManager。WindowManager裏面主要是addView,removeView,updateViewLayout這幾個方法來顯示View,以及通過WindowManager.LayoutParams這個API來設置相關的屬性。下文講詳細介紹。

WindowsManager

1、WindowsManager使用方法

1

//獲取WindowManager對象
WindowManager wManager = getApplicationContext().getSystemService(Context.WINDOW_ SERVICE);
//獲得WindowManager.LayoutParams對象,爲後續操作作準備
WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();
//...WindowManager.LayoutParams一頓設置
wManager.addView(textView, wmParams);

2

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Button floatingButton = new Button(this);
        floatingButton.setText("button");
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                0, 0,
                PixelFormat.TRANSPARENT
        );
        // flag 設置 Window 屬性
        layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        // type 設置 Window 類別(層級)
        layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
        layoutParams.gravity = Gravity.CENTER;
        WindowManager windowManager = getWindowManager();
        windowManager.addView(floatingButton, layoutParams);
    }
}

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

3全屏設置

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //去除標題欄
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //去除狀態欄
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_main);//要放到加載佈局文件代碼之前
        initView();
        initData();
        initListener();
}

2、WindowManager的關聯類

2.1、 WindowManager

先來看看WindowManager裏面有什麼。
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManager.java

public interface WindowManager extends ViewManager {

    int DOCKED_INVALID = -1;
    int DOCKED_LEFT = 1;
    int DOCKED_TOP = 2;
    int DOCKED_RIGHT = 3;
    int DOCKED_BOTTOM = 4;
    final static String INPUT_CONSUMER_PIP = "pip_input_consumer";
    final static String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer";
    final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
 
    public static class BadTokenException extends RuntimeException {
        public BadTokenException() {}
        public BadTokenException(String name) { super(name);}
    }
        
    public static class InvalidDisplayException extends RuntimeException {
        public InvalidDisplayException() {}
        public InvalidDisplayException(String name) { super(name);}
    }

    public Display getDefaultDisplay();
    public void removeViewImmediate(View view);
    public interface KeyboardShortcutsReceiver {
        void onKeyboardShortcutsReceived(List<KeyboardShortcutGroup> result);
    }

    final int TAKE_SCREENSHOT_FULLSCREEN = 1;
    final int TAKE_SCREENSHOT_SELECTED_REGION = 2;
    public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";

    public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver, int deviceId);

	public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
	......
	}
	......
    
 }

WindowManager是一個接口,繼承ViewManager。裏面有一些實例屬性和方法,還有一個LayoutParams佔據着很大一部分。LayoutParams是一個序列化類。而且WindowManager裏面的大部分都是隱藏的屬性和方法,只給系統調用。

2.2、ViewManager

WindowManager繼承ViewManager。ViewManager裏面實際上只有三個方法:addViewupdateViewLayoutremoveView,分別對應着添加view、更新viewlayout、移走view。

\frameworks\base\core\java\android\view\ViewManager.java

package android.view;
public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

2.3、WindowManagerImpl

那麼WindowManager的實現類是哪個?是WindowManagerImpl。
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerImpl.java

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

    private IBinder mDefaultToken;

    public WindowManagerImpl(Context context) {
        this(context, null);
    }

    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

    public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
        return new WindowManagerImpl(displayContext, mParentWindow);
    }

    public void setDefaultToken(IBinder token) {
        mDefaultToken = token;
    }

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }

    private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
        // Only use the default token if we don't have a parent window.
        if (mDefaultToken != null && mParentWindow == null) {
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }

            // Only use the default token if we don't already have a token.
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (wparams.token == null) {
                wparams.token = mDefaultToken;
            }
        }
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

    @Override
    public void requestAppKeyboardShortcuts(
            final KeyboardShortcutsReceiver receiver, int deviceId) {
        IResultReceiver resultReceiver = new IResultReceiver.Stub() {
            @Override
            public void send(int resultCode, Bundle resultData) throws RemoteException {
                List<KeyboardShortcutGroup> result =
                        resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);
                receiver.onKeyboardShortcutsReceived(result);
            }
        };
        try {
            WindowManagerGlobal.getWindowManagerService()
                .requestAppKeyboardShortcuts(resultReceiver, deviceId);
        } catch (RemoteException e) {
        }
    }

    @Override
    public Display getDefaultDisplay() {
        return mContext.getDisplay();
    }

    @Override
    public Region getCurrentImeTouchRegion() {
        try {
            return WindowManagerGlobal.getWindowManagerService().getCurrentImeTouchRegion();
        } catch (RemoteException e) {
        }
        return null;
    }
}

可以看到實現類裏面主要的參數是下面這幾個。

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

    private IBinder mDefaultToken;

裏面最主要的是WindowManagerGlobal了,view的增刪改都是由它完成的。
WindowManagerGlobal裏面主要管理是一下一些參數。view的相關控制用ArrayList裝載着。
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerGlobal.java

    private static WindowManagerGlobal sDefaultWindowManager;
    private static IWindowManager sWindowManagerService;
    private static IWindowSession sWindowSession;

    private final Object mLock = new Object();

    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

    private Runnable mSystemPropertyUpdater;

addView
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerGlobal.java

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

updateViewLayout
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerGlobal.java

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }

removeView
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerGlobal.java

    public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }

            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }

3、WindowManager 的一些屬性

WindowManager.LayoutParams屬性裏面主要有Type屬性和Flags屬性。

Type表示Window的類型,Window有三種類型,分別是應用窗口、子窗口和系統窗口。
\frameworks\base\core\java\android\view\ViewManager.java

        /**
         * Start of window types that represent normal application windows.
         */
        public static final int FIRST_APPLICATION_WINDOW = 1;
        public static final int TYPE_BASE_APPLICATION   = 1;//一個應用程序窗口,作爲“基礎”窗口  
        public static final int TYPE_APPLICATION        = 2;//正常的應用程序窗口
        public static final int TYPE_APPLICATION_STARTING = 3;//特殊應用程序窗口顯示
        public static final int TYPE_DRAWN_APPLICATION = 4;//顯示前繪畫
        public static final int LAST_APPLICATION_WINDOW = 99;//End of types of application windows.
        
        //子窗口
        public static final int FIRST_SUB_WINDOW = 1000;
        public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;//應用面板
        public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;//窗口顯示媒體(如視頻)
        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;//應用程序窗口的頂部
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;//類似TYPE_APPLICATION_PANEL,佈局爲頂級窗口
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;//顯示媒體層疊窗口
        public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;//上面一個sub-panel之上的應用程序的窗口        
        public static final int LAST_SUB_WINDOW = 1999;//子系統結尾

        /**
         * Start of system-specific window types.  
         */
        public static final int FIRST_SYSTEM_WINDOW     = 2000;//These are not normally created by applications.
        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;//狀態欄。只能有一個狀態欄 
        public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;//搜索欄。只能有一個搜索欄
        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;//電話
        public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;//系統窗口,如低功率警覺
        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;//鍵盤守衛窗口
        public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;//臨時通知。
        public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;//系統覆蓋窗口,它需要顯示出來
        public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;//優先電話界面,需要即使顯示
        public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;//從狀態欄面板幻燈片 
        public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;//鍵盤守衛顯示對話
        public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;//內部系統錯誤窗口,出現在上面 
        public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;//內部輸入法窗口,上面出現
        public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;//內部輸入方法對話框窗口,上面出現
        public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;//壁紙窗口,放在任何想要的窗口 
        public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;//面板幻燈片從狀態欄 
        public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;//安全系統覆蓋窗口
        public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;//拖動圖層,放在最上面
        public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;//從狀態欄下面板,幻燈片
        public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;//(鼠標)指針 
        public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;//導航欄(有別於狀態欄時) 
        public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;//音量級別覆蓋/對話框顯示
        public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;//引導進度對話框,在所有窗口之上
        public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;//當系統UI隱藏時候輸入事件
        public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;//屏保窗口
        public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;//導航欄面板
        public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;//顯示窗口覆蓋
        public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;//放大窗口覆蓋
        public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;//私有虛擬顯示在頂部的描述
        public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;//聲音交互
        public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
        public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
        public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
        public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
        public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
        public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;
        public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
        public static final int LAST_SYSTEM_WINDOW      = 2999;//系統窗口結尾

應用窗口從1開始,到99。子窗口範圍爲1000 ~ 1999 ,系統窗口範圍爲2000~2999。子窗口表示依賴於應用窗口的窗口,比如PopupWindow就是依賴於應用的子窗口。Android系統有個X,Y,Z軸的座標體系。X表示橫軸,Y表示豎軸,Z表示垂直於平面的軸。這些窗口數值對應的事Z軸。數字越大表示越靠近用戶。從上面的數值可以看出,系統窗口是最靠近用戶的,其次是子窗口,最後是應用窗口。
Flags參數表示Window的屬性,控制着Window的顯示特性。

       FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
       FLAG_DIM_BEHIND
       FLAG_NOT_FOCUSABLE//不獲取焦點,不接收各種輸入事件,由後面的窗口得到焦點
       FLAG_NOT_TOUCHABLE
       FLAG_NOT_TOUCH_MODAL//不獲取觸摸事件,由後面的窗口得到觸摸事件
       FLAG_TOUCHABLE_WHEN_WAKING
       FLAG_KEEP_SCREEN_ON
       FLAG_LAYOUT_IN_SCREEN
       FLAG_LAYOUT_NO_LIMITS
       FLAG_FULLSCREEN
       FLAG_FORCE_NOT_FULLSCREEN
       FLAG_SECURE
       FLAG_SCALED
       FLAG_IGNORE_CHEEK_PRESSES
       FLAG_LAYOUT_INSET_DECOR
       FLAG_ALT_FOCUSABLE_IM
       FLAG_WATCH_OUTSIDE_TOUCH
       FLAG_SHOW_WHEN_LOCKED	//讓Window顯示在鎖屏的界面上
       FLAG_SHOW_WALLPAPER
       FLAG_TURN_SCREEN_ON
       FLAG_DISMISS_KEYGUARD
       FLAG_SPLIT_TOUCH
       FLAG_HARDWARE_ACCELERATED
       FLAG_LOCAL_FOCUS_MODE
       FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS

Flags標識按位來增加,比如下面這這些。當幾個功能疊加的時候可以用或運算。
flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE

public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;
public static final int FLAG_DIM_BEHIND       	 			= 0x00000002;
public static final int FLAG_BLUR_BEHIND        			= 0x00000004;
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_TOUCHABLE_WHEN_WAKING 			= 0x00000040;
public static final int FLAG_KEEP_SCREEN_ON     			= 0x00000080;
public static final int FLAG_LAYOUT_IN_SCREEN   			= 0x00000100;
......
public static final int FLAG_SHOW_WHEN_LOCKED 				= 0x00080000;
......

不止這兩個屬性設置,還有softInputMode,gravity,horizontalMargin,verticalMargin,screenBrightness,buttonBrightness,rotationAnimation等等的參數設置。WindowManager.LayoutParams裏面有addFlagsclearFlagssetColorMode等來控制這些屬性的設置。
softInputMode是軟鍵盤設置,可以看下它的幾個參數。

            SOFT_INPUT_STATE_UNSPECIFIED,
            SOFT_INPUT_STATE_UNCHANGED,
            SOFT_INPUT_STATE_HIDDEN,
            SOFT_INPUT_STATE_ALWAYS_HIDDEN,
            SOFT_INPUT_STATE_VISIBLE,
            SOFT_INPUT_STATE_ALWAYS_VISIBLE,
            SOFT_INPUT_ADJUST_UNSPECIFIED,
            SOFT_INPUT_ADJUST_RESIZE,
            SOFT_INPUT_ADJUST_PAN,
            SOFT_INPUT_ADJUST_NOTHING,
            SOFT_INPUT_IS_FORWARD_NAVIGATION,
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章