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获取这一信息并据此对窗口进行布局,或设置状态栏与导航栏的可见性。
 

转载于:https://my.oschina.net/u/920274/blog/3063436

你可能感兴趣的:(Android源码笔记--SystemUIVisibility)