Android 12系统源码_SystemUI(八)SystemUIVisibility属性

前言

在Android系统中,很多应用都需要根据具体情况来控制状态栏和导航栏的显示和隐藏,又或者将状态栏透明,实现诸如沉浸式、全面屏灯效果,而要实现这些效果,都离不开SystemUIVisibility属性。由于SystemUIVisibilityy属性主要用来控制系统状态栏和导航栏的行为,而状态栏和导航栏都属于SystemUI模块的StatusBar,所以SystemUIVisibility属性的消费者肯定包含StatusBar。另外当状态栏和导航栏发生变化的时候,窗口的布局一般也会跟着发生变化,这就意味着窗口管理者PhoneWindowManager肯定也要消费SystemUIVisibility属性。本篇文章我们就来具体分析一下和这个属性有关的代码。

一、控制状态栏和底部栏行为

1.1、使用SystemUIVisibility属性控制

1、为窗口设置SystemUIVisibility属性的方式有两种,一种是直接在窗口的WindowManager.LayoutParams对象的systemUiVisibility属性上进行设置,并通过WindowManager.updateViewLayout()方法使其生效。

frameworks/base/core/java/android/view/WindowManager.java

public interface WindowManager extends ViewManager {
   
   
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
   
   
    	//隐藏窗口的所有装饰,比如状态栏和导航栏
        public static final int FLAG_FULLSCREEN      = 0x00000400;

		//控制窗口状态栏、导航栏的显示和隐藏
        public int systemUiVisibility;
    }
}    

2、另一种是在一个已经显示在窗口上的控件中调用setSystemUiVisibility方法,传入如下属性。

frameworks/base/core/java/android/view/View.java

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
   
   
//隐藏导航栏
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
//隐藏状态栏
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
}

这种方式最终影响的其实是窗口的WindowManager.LayoutParams对象的subtreeSystemUiVisibility属性。

public interface WindowManager extends ViewManager {
   
   
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
   
   
		//控制窗口状态栏、导航栏的显示和隐藏
        public int subtreeSystemUiVisibility;
    }
}    

窗口的状态栏导航栏显示与否,最终其实是受以上两个属性共同影响的。

1.2、使用PhoneWindow的setDecorFitsSystemWindows方法

页面想要实现状态栏和底部栏的沉浸式效果,除了使用SystemUIVisibility属性,还可以调用当前页面对应的Window的setDecorFitsSystemWindows方法。

public class PhoneWindow extends Window implements MenuBuilder.Callback {
   
   

    //窗口的内容区域是否自适应系统装饰窗口(例如状态栏和导航栏)的显示区域。
    //如果设置为true,当前窗口的内容将自动避开系统装饰窗口区域,以避免视图内容被系统栏遮挡。
    boolean mDecorFitsSystemWindows = true;
    
    /**
     * @param decorFitsSystemWindows false沉浸式系统栏 true非沉浸式系统栏
     */
    @Override
    public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
   
   
        mDecorFitsSystemWindows = decorFitsSystemWindows;
        applyDecorFitsSystemWindows();
    }
    
}

二、View的setSystemUiVisibility方法调用流程

1、View的setSystemUiVisibility方法如下所示。

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
   
   
        
    int mSystemUiVisibility;
    protected ViewParent mParent;
    
    public void setSystemUiVisibility(int visibility) {
   
   
        if (visibility != mSystemUiVisibility) {
   
   
            mSystemUiVisibility = visibility;//保存SystemUIVisibility属性
            if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
   
   
                mParent.recomputeViewAttributes(this);//通知父控件子控件属性发生了变化
            }
        }
    }
}

setSystemUiVisibility方法首先将属性赋值给mSystemUiVisibility,然后会调用父控件的recomputeViewAttributes方法,通知父控件子控件属性发生了变化。ViewParent是一个接口,在Android中有两个类实现了这个接口,它们分别是ViewGroup和ViewRootImpl。

2、ViewGroup和ViewRootImpl和recomputeViewAttributes方法相关的代码如下所示。

frameworks/base/core/java/android/view/View.java

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
   
   
    @Override
    public void recomputeViewAttributes(View child) {
   
   
        if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
   
   
            ViewParent parent = mParent;
            if (parent != null) parent.recomputeViewAttributes(this);
        }
    }
}

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {
   
   
    
    final View.AttachInfo mAttachInfo;
    @Override
    public void recomputeViewAttributes(View child) {
   
   
        checkThread();//检测线程是不是UI线程
        if (mView == child) {
   
   
            mAttachInfo.mRecomputeGlobalAttributes = true;//标记需要重新计算本地属性
            if (!mWillDrawSoon) {
   
   
                scheduleTraversals();//进一步调用scheduleTraversals方法。
            }
        }
    }
}

结合Android 12系统源码_窗口管理(二)WindowManager对窗口的管理过程,我们知道包括Activity的根布局DecorView在内的任何View,WindowManager在将它添加到窗口上的过程中,最终都会创建一个ViewRootImpl,并将View设置给ViewRootImpl,这样根View的父类就变成了ViewRootImpl。这就意味着不管任何子View调用recomputeViewAttributes方法,最终所触发的都是ViewRootImpl的recomputeViewAttributes,而ViewRootImpl会进一步调用scheduleTraversals方法。

3、ViewRootImpl和scheduleTraversals方法相关的代码如下所示。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {
   
   
        
     final Choreographer mChoreographer;//编舞者
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();//回调对象

    void scheduleTraversals() {
   
   
        if (!mTraversalScheduled) {
   
   
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    
   final class TraversalRunnable implements Runnable {
   
   
        @Override
        public void run() {
   
   
            doTraversal();//继续执行doTraversal
        }
    }

    void doTraversal() {
   
   
        if (mTraversalScheduled) {
   
   
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
   
   
                Debug.startMethodTracing("ViewAncestor");
            }
			//执行performTraversals
            performTraversals();

            if (mProfile) {
   
   
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
}

scheduleTraversals方法会为编舞者对象设置回调,最终会等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法,该方法会调用doTraversal方法,然后进一步调用performTraversals方法。

4、ViewRootImpl的performTraversals方法代码逻辑非常多,这里只列出了我们需要关注的代码。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {
   
   
        
    final IWindowSession mWindowSession;//和WMS通信的Binder对象,具体为Session对象
	public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性
    final View.AttachInfo mAttachInfo;//控件信息
    
    private void performTraversals() {
   
   
        final View host = mView;
        if (host == null || !mAdded) {
   
   
            return;
        }
        if (mWaitForBlastSyncComplete) {
   
   
            mRequestedTraverseWhilePaused = true;
            return;
        }

        mIsInTraversal = true;
        mWillDrawSoon = true;
        boolean windowSizeMayChange = false;
        WindowManager.LayoutParams lp = mWindowAttributes;//将当前窗口的最新属性赋值给lp
        int desiredWindowWidth;
        int desiredWindowHeight;
        final int viewVisibility = getHostVisibility();
        final boolean viewVisibilityChanged = !mFirst
                && (mViewVisibility != viewVisibility || mNewSurfaceNeeded
                || mAppVisibilityChanged);
        mAppVisibilityChanged = false;
        final boolean viewUserVisibilityChanged = !mFirst &&
                ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
        WindowManager.LayoutParams params = null;
        ...代码省略...
        //收集mView的属性,判断是否需要更新params
        if (collectViewAttributes()) {
   
   
            params = lp;
        }
        ...代码省略...
        //此方法最终会触发WindowManagerService的relayoutWindow方法
		relayoutWindow(params, viewVisibility, insetsPending);
       	...代码省略...  
       	//测量
     	performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
     	...代码省略...  
     	//布局
     	performLayout(lp, mWidth, mHeight);
     	...代码省略...  
     	//绘制
        performDraw(</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值