Android源碼筆記--SystemUIVisibility

SystemUIVisibility     java

       最近在學習SystemUI時,涉及到了SystemUIVisibility,在此記錄一下。雖然StatusBarManager以及StatusBarManagerService爲應用程序以及系統服務提供了操做狀態欄與導航欄的全部接口,可是這些接口並不適用於那些沒有系統簽名的普通應用程序。若是普通應用程序但願對狀態欄以及導航欄進行操做,就須要使用SystemUIVisibility機制。session

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

       常見設置SystemUIVisibility的方式有兩種。一是在任意一個已經顯示在窗口上的控件調用View.setSystemUiVisibility(),二是直接在窗口的LayoutParams.systemUiVisibility上進行設置並經過WindowManager.updateViewLayout()方法使其生效。ide

       SystemUIVisibility在系統中存在的地方佈局

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

 1  控件樹中的SystemUIVisibilitythis

View.setSystemUiVisibility()的實現spa

        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);
                }
            }
        }.net

    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獲取這一信息並據此對窗口進行佈局,或設置狀態欄與導航欄的可見性。  

相關文章
相關標籤/搜索