1. 程式人生 > >Android原始碼筆記--SystemUIVisibility

Android原始碼筆記--SystemUIVisibility

SystemUIVisibility     

       最近在學習SystemUI時,涉及到了SystemUIVisibility,在此記錄一下。雖然StatusBarManager以及StatusBarManagerService為應用程式以及系統服務提供了操作狀態列與導航欄的所有介面,但是這些介面並不適用於那些沒有系統簽名的普通應用程式。如果普通應用程式希望對狀態列以及導航欄進行操作,就需要使用SystemUIVisibility機制。

    View.java
    //隱藏導航欄
    public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
    //隱藏狀態列
    public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;

       常見設定SystemUIVisibility的方式有兩種。一是在任意一個已經顯示在視窗上的控制元件呼叫View.setSystemUiVisibility(),二是直接在視窗的LayoutParams.systemUiVisibility上進行設定並通過WindowManager.updateViewLayout()方法使其生效。

       SystemUIVisibility在系統中存在的地方

        SystemUIVisibility主要涉及狀態列和導航欄的行為以及窗口布局兩個方面。因此它的消費者包含SystemUI中BaseStatusBar以及負責窗口布局的PhoneWindowManger。

 1  控制元件樹中的SystemUIVisibility

View.setSystemUiVisibility()的實現

        View.java
     
        public void setSystemUiVisibility(int visibility) {
            if (visibility != mSystemUiVisibility) {
               //(1) 儲存在View自己的成員變數mSystemUiVisibility.
                mSystemUiVisibility = visibility;
                //(2)通知父控制元件這一變化
                if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
                    mParent.recomputeViewAttributes(this);
                }
            }
        }

    ViewRootImpl.java
        
          @Override
        public void recomputeViewAttributes(View child) {
       // 需要在視窗的主執行緒中呼叫
            checkThread();
            if (mView == child) {
        // 2  標記需要處理SystemUIVisibility的變化
               mAttachInfo.mRecomputeGlobalAttributes = true;
                if (!mWillDrawSoon) {
        // 3 觸發一次“遍歷”動作
                    scheduleTraversals();
                }
            }
        }

分析:遍歷過程中會執行ViewRootImpl.collectViewAttributes()方法收集控制元件樹中每個View所儲存的SystemUIVisibility. 如下:

    private boolean collectViewAttributes() {
            if (mAttachInfo.mRecomputeGlobalAttributes) {
                //Log.i(mTag, "Computing view hierarchy attributes!");
                mAttachInfo.mRecomputeGlobalAttributes = false;
                boolean oldScreenOn = mAttachInfo.mKeepScreenOn;
                mAttachInfo.mKeepScreenOn = false;
            //清空所儲存的SystemUiVisibility
               mAttachInfo.mSystemUiVisibility = 0;
                mAttachInfo.mHasSystemUiListeners = false;
            //遍歷整個控制元件樹
                mView.dispatchCollectViewAttributes(mAttachInfo, 0);
            //移除被禁用的SystemUiVisibility標記    
               mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility;
                WindowManager.LayoutParams params = mWindowAttributes;
                mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
                if (mAttachInfo.mKeepScreenOn != oldScreenOn
                        || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
                        || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
                    applyKeepScreenOnFlag(params);
            //將SystemUiVisibility儲存到視窗的LayoutParams
                   params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
                    params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners;
                    mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility);
                    return true;
                }
            }
            return false;
        }

     分析:當ViewRootImpl通過WMS.reLayoutWindow()方法對視窗進行重新佈局時,本視窗所期望的SUV會伴隨著LayoutParams

.subtreeSystemUiVisibility以及LayoutParams.sytemUiVisibility兩個欄位進入WMS.

     WindowManagerService.java
     
             public int relayoutWindow(Session session, IWindow client, int seq,
                WindowManager.LayoutParams attrs, int requestedWidth,
                int requestedHeight, int viewVisibility, int flags,
                Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
                Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
                Configuration outConfig, Surface outSurface) {
                
                .................
                
                   synchronized(mWindowMap) {
                WindowState win = windowForClientLocked(session, client, false);
                if (win == null) {
                    return 0;
                }
     
                WindowStateAnimator winAnimator = win.mWinAnimator;
                if (viewVisibility != View.GONE) {
                    win.setRequestedSize(requestedWidth, requestedHeight);
                }
     
                int attrChanges = 0;
                int flagChanges = 0;
                if (attrs != null) {
                    mPolicy.adjustWindowParamsLw(attrs);
                    // if they don't have the permission, mask out the status bar bits
                    if (seq == win.mSeq) {
                        int systemUiVisibility = attrs.systemUiVisibility
                                | attrs.subtreeSystemUiVisibility;
                        if ((systemUiVisibility & DISABLE_MASK) != 0) {
                            if (!hasStatusBarPermission) {
                                systemUiVisibility &= ~DISABLE_MASK;
                            }
                        }
                        //儲存到WindowState.mSystemUiVisibility
                        win.mSystemUiVisibility = systemUiVisibility;
                    }
                    if (win.mAttrs.type != attrs.type) {
                        throw new IllegalArgumentException(
                                "Window type can not be changed after the window is added.");
                    }
                
                ........
                }

        在此,PhoneWindowManager可以通過WindowState.getSystemUiVisibility獲取這一資訊並據此對視窗進行佈局,或設定狀態列與導航欄的