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

这篇具有很好参考价值的文章主要介绍了Android 12系统源码_SystemUI(八)SystemUIVisibility属性。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

一、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;
    }
}    

窗口的状态栏导航栏显示与否,最终其实是受以上两个属性共同影响的。接下来我们具体来分析一下View的setSystemUiVisibility方法是如何生效的。

二、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();
        ...代码省略...  
     }

    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
           //调用IWindowSession的relayout方法,该方法最终会触发WindowManagerService的relayoutWindow方法
        int relayoutResult = mWindowSession.relayout(mWindow, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mSurfaceSize);        
     }
  }

performTraversals首先调用collectViewAttributes方法收集所有子View的属性,然后调用relayoutWindow方法,该方法最终会触发WindowManagerService的relayoutWindow方法,然后回继续调用触发View测量的performMeasure方法,触发View布局的performLayout方法和触发View绘制的performDraw方法。

三、获取最新的SystemUIVisibility属性

1、ViewRootImpl的collectViewAttributes方法是一个很关键的方法,此方法会重新计算最新的SystemUIVisibility属性。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {

	public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性
    final View.AttachInfo mAttachInfo;//控件信息
    
    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最新的的SystemUI属性,赋值给mAttachInfo
            mView.dispatchCollectViewAttributes(mAttachInfo, 0);
         	...代码省略...
        }
        return false;
    }
 }

collectViewAttributes首先会清空当前窗口视图mView已经存在的SystemUiVisibility属性,然后调用View的dispatchCollectViewAttributes方法重新获取最新的的SystemUiVisibility属性。

2、View的dispatchCollectViewAttributes方法如下所示。

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    
    int mSystemUiVisibility;
    
    void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
        performCollectViewAttributes(attachInfo, visibility);
    }
    
    void performCollectViewAttributes(AttachInfo attachInfo, int visibility) {
        if ((visibility & VISIBILITY_MASK) == VISIBLE) {
            if ((mViewFlags & KEEP_SCREEN_ON) == KEEP_SCREEN_ON) {
                attachInfo.mKeepScreenOn = true;
            }
            //将最新的systemuivisiblity赋予AttachInfo的mSystemUiVisibility 属性
            attachInfo.mSystemUiVisibility |= mSystemUiVisibility;
            //设置最新的SystemUiVisibility监听对象,如果不为空,则将AttachInfo的mHasSystemUiListeners属性设置为true。
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
                attachInfo.mHasSystemUiListeners = true;
            }
        }
    }

}    

3、接着看ViewRootImpl的collectViewAttributes方法。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {

	public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性
    final View.AttachInfo mAttachInfo;//控件信息
    
    private boolean collectViewAttributes() {
  		//判断是否需要重新计算本地属性
        if (mAttachInfo.mRecomputeGlobalAttributes) {
         	...代码省略...
            //移除被禁用的SystemUiVisibility属性
            mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility;
            //让params引用指向mWindowAttributes对象
            WindowManager.LayoutParams params = mWindowAttributes;
            mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
            mCompatibleVisibilityInfo.globalVisibility =
                    (mCompatibleVisibilityInfo.globalVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE)
                            | (mAttachInfo.mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
            dispatchDispatchSystemUiVisibilityChanged(mCompatibleVisibilityInfo);
            if (mAttachInfo.mKeepScreenOn != oldScreenOn
                    || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
                    || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
                applyKeepScreenOnFlag(params);
                //将重新获取的窗口视图mView的SystemUiVisibility保存到窗口的LayoutParams属性中
                params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
                params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners;
                //调用View的dispatchWindowSystemUiVisiblityChanged方法
                mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility);
                return true;
            }
        }
        return false;
    }
 }

在重新获得mView的SystemUiVisibility属性之后,首先会从该属性中移除被禁用的SystemUiVisibility属性,然后让params引用指向mWindowAttributes对象,并将重新获取的保存在mAttachInfo对象中的SystemUiVisibility属性保存到当前窗口的LayoutParams属性中,最后会调用当前View的dispatchWindowSystemUiVisiblityChanged方法。

4、View的dispatchWindowSystemUiVisiblityChanged方法如下所示。

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    @Deprecated
    public void dispatchWindowSystemUiVisiblityChanged(int visible) {
        onWindowSystemUiVisibilityChanged(visible);//调用onWindowSystemUiVisibilityChanged方法
    }
    public void onWindowSystemUiVisibilityChanged(int visible) {
    	//默认为空实现
    }
}

该方法会进一步调用onWindowSystemUiVisibilityChanged方法,onWindowSystemUiVisibilityChanged方法默认为空实现,但是如果当前mView为DecorView时则不同,DecorView实现了此方法。

四、DecorView更新状态栏和导航栏背景颜色

1、DecorView的onWindowSystemUiVisibilityChanged方法如下所示。

frameworks/base/core/java/com/android/internal/policy/DecorView.java

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {

    @Override
    public void onWindowSystemUiVisibilityChanged(int visible) {
    	//调用updateColorViews方法
        updateColorViews(null /* insets */, true /* animate */);
        updateDecorCaptionStatus(getResources().getConfiguration());
        if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
            updateStatusGuardColor();
        }
    }

}

onWindowSystemUiVisibilityChanged方法会调用一个updateColorViews这个关键方法。
2、updateColorViews会调用updateColorViewInt方法更新导航栏和状态栏的背景颜色。

    public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    
    WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
    		...代码省略...
            //更新导航栏颜色,mNavigationColorViewState为导航栏的相关筛选条件
            Log.d("SystemBar", "updateColorViews mNavigationColorViewState="+mNavigationColorViewState);
            updateColorViewInt(mNavigationColorViewState, calculateNavigationBarColor(appearance),
                    mWindow.mNavigationBarDividerColor, navBarSize,
                    navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
                    0 /* sideInset */, animate && !disallowAnimate,
                    mForceWindowDrawsBarBackgrounds, controller);
            boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground;
            mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
                    && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
            if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
                if (viewRoot != null) {
                    viewRoot.requestInvalidateRootRenderNode();
                }
            }
    		...代码省略...
            boolean statusBarNeedsRightInset = navBarToRightEdge
                    && mNavigationColorViewState.present;
            boolean statusBarNeedsLeftInset = navBarToLeftEdge
                    && mNavigationColorViewState.present;
            int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
                    : statusBarNeedsLeftInset ? mLastLeftInset : 0;
            int statusBarColor = calculateStatusBarColor(appearance);
            //更新状态栏颜色,mStatusColorViewState为状态栏的相关筛选条件
            Log.d("SystemBar", "updateColorViews mStatusColorViewState="+mStatusColorViewState);
            updateColorViewInt(mStatusColorViewState, statusBarColor, 0,
                    mLastTopInset, false /* matchVertical */, statusBarNeedsLeftInset,
                    statusBarSideInset, animate && !disallowAnimate,
                    mForceWindowDrawsBarBackgrounds, controller);
    		...代码省略...
    }
        
}

关于DecorView更新状态栏、导航栏背景颜色的过程,后续篇章会具体分析。

五、WindowManagerService的relayoutWindow方法

1、重新回到第二节第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() {
        ...代码省略...
        //收集mView的属性,判断是否需要更新params
        if (collectViewAttributes()) {
            params = lp;
        }
        ...代码省略...
        //此方法最终会触发WindowManagerService的relayoutWindow方法
		relayoutWindow(params, viewVisibility, insetsPending);
       	...代码省略...  
       	//测量
     	performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
     	...代码省略...  
     	//布局
     	performLayout(lp, mWidth, mHeight);
     	...代码省略...  
     	//绘制
        performDraw();
        ...代码省略...  
     }

    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
           //调用IWindowSession的relayout方法,该方法最终会触发WindowManagerService的relayoutWindow方法
        int relayoutResult = mWindowSession.relayout(mWindow, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mSurfaceSize);        
     }
  }

在调用collectViewAttributes获取最新的systemUIVisibiliy属性之后,会调用relayoutWindow方法,该方法进一步调用IWindowSession的relayout方法,IWindowSession的具体实现类为Session。
2、Session的relayout方法如下所示。

frameworks/base/services/core/java/com/android/server/wm/Session.java

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {

    final WindowManagerService mService;
    
    @Override
    public int relayout(IWindow window, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
            ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
        int res = mService.relayoutWindow(this, window, attrs,
                requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
                outActiveControls, outSurfaceSize);
        return res;
    }
}

relayout方法会进一步调用WindowManagerService的relayoutWindow方法。

3、WindowManagerService的relayoutWindow方法如下所示。

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
        
    public int relayoutWindow(Session session, IWindow client, LayoutParams attrs//窗口属性
                              , int requestedWidth, int requestedHeight, int viewVisibility//根View控件是否可见
                              , int flags, long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
                              SurfaceControl outSurfaceControl, InsetsState outInsetsState,
                              InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
            ...代码省略...
           final WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }
            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
            ...代码省略...
            //是否应该重新布局
            final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                    (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                            || win.mActivityRecord.isClientVisible());
           ...代码省略...
           if (shouldRelayout) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
                //标记
                result = win.relayoutVisibleWindow(result);
                if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                    //焦点可能变化了
                    focusMayChange = true;
                }
                if (win.mAttrs.type == TYPE_INPUT_METHOD
                        && displayContent.mInputMethodWindow == null) {
                    displayContent.setInputMethodWindowLocked(win);
                    imMayMove = true;
                }
                win.adjustStartingWindowFlags();
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            }  
            ...代码省略... 
            if (focusMayChange) {
                //relayoutWindow add by syl
                Log.d(TAG, "SystemBar: WindowManagerService_relayoutWindow");
                if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
                    imMayMove = false;
                }
            }   
            ...代码省略...           
	}
}

relayoutWindow方法会调用一个关键方法updateFocusedWindowLocked。

4、WindowManagerService的updateFocusedWindowLocked方法如下所示。

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
   
   RootWindowContainer mRoot;

    //更新窗口焦点的变化
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        Log.d("SystemBar", "WindowManagerService: updateFocusedWindowLocked");
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
        boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        return changed;
    }
}

此方法会进一步调用RootWindowContainer的updateFocusedWindowLocked方法。

5、RootWindowContainer的updateFocusedWindowLocked方法如下所示。
/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

class RootWindowContainer extends WindowContainer<DisplayContent>
        implements DisplayManager.DisplayListener {
    // 当窗口焦点发生变化的时候,更新窗口焦点的变化
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        mTopFocusedAppByProcess.clear();
        boolean changed = false;
        int topFocusedDisplayId = INVALID_DISPLAY;
        for (int i = mChildren.size() - 1; i >= 0; --i) {
        	//循环遍历DisplayContent的updateFocusedWindowLocked方法
            final DisplayContent dc = mChildren.get(i);
            changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
            final WindowState newFocus = dc.mCurrentFocus;
            if (newFocus != null) {
                final int pidOfNewFocus = newFocus.mSession.mPid;
                if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
                    mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);
                }
                if (topFocusedDisplayId == INVALID_DISPLAY) {
                    topFocusedDisplayId = dc.getDisplayId();
                }
            } else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
                // The top-most display that has a focused app should still be the top focused
                // display even when the app window is not ready yet (process not attached or
                // window not added yet).
                topFocusedDisplayId = dc.getDisplayId();
            }
        }
        if (topFocusedDisplayId == INVALID_DISPLAY) {
            topFocusedDisplayId = DEFAULT_DISPLAY;
        }
        if (mTopFocusedDisplayId != topFocusedDisplayId) {
            mTopFocusedDisplayId = topFocusedDisplayId;
            mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
            mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
            mWmService.mAccessibilityController.setFocusedDisplay(topFocusedDisplayId);
            ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId);
        }
        return changed;
    }

6、DisplayContent 的updateFocusedWindowLocked方法如下所示。

class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {

    //当窗口焦点发生变化的时候,更新窗口焦点的变化 
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
            int topFocusedDisplayId) {
        WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
        if (mCurrentFocus == newFocus) {
            return false;
        }
        boolean imWindowChanged = false;
        final WindowState imWindow = mInputMethodWindow;
        if (imWindow != null) {
            final WindowState prevTarget = mImeLayeringTarget;
            final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
            imWindowChanged = prevTarget != newTarget;

            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
                    && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                assignWindowLayers(false /* setLayoutNeeded */);
            }

            if (imWindowChanged) {
                mWmService.mWindowsChanged = true;
                setLayoutNeeded();
                newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
            }
        }

        ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
                mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
        final WindowState oldFocus = mCurrentFocus;
        mCurrentFocus = newFocus;

        if (newFocus != null) {
            mWinAddedSinceNullFocus.clear();
            mWinRemovedSinceNullFocus.clear();

            if (newFocus.canReceiveKeys()) {
                // Displaying a window implicitly causes dispatching to be unpaused.
                // This is to protect against bugs if someone pauses dispatching but
                // forgets to resume.
                newFocus.mToken.paused = false;
            }
        }
		//触发DisplayPolicy的focusChangedLw方法。
        getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
		...代码省略...
    }

}

7、DisplayPolicy的focusChangedLw方法如下所示。

public class DisplayPolicy {
    /**
     * A new window has been focused.
     */
    public void focusChangedLw(WindowState lastFocus, WindowState newFocus) {
      	//焦点发生了变化
        mFocusedWindow = newFocus;
        mLastFocusedWindow = lastFocus;
        if (mDisplayContent.isDefaultDisplay) {
            mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
        }
        //更新SystemBar的属性
        updateSystemBarAttributes();
    }
}

DisplayPolicy的focusChangedLw方法最终会调用updateSystemBarAttributes方法来刷新系统栏属性。

8、DisplayPolicy的updateSystemBarAttributes的方法如下所示。

public class DisplayPolicy {

    //更新SystemBar的属性
    void updateSystemBarAttributes() {
        WindowState winCandidate = mFocusedWindow;//焦点窗口
        if (winCandidate == null && mTopFullscreenOpaqueWindowState != null
                && (mTopFullscreenOpaqueWindowState.mAttrs.flags
                & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) {
            //只有焦点窗口才能控制系统栏
            winCandidate = mTopFullscreenOpaqueWindowState;
        }
        // If there is no window focused, there will be nobody to handle the events
        // anyway, so just hang on in whatever state we're in until things settle down.
        if (winCandidate == null) {
            return;
        }

        // The immersive mode confirmation should never affect the system bar visibility, otherwise
        // it will unhide the navigation bar and hide itself.
        if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {

            // The immersive mode confirmation took the focus from mLastFocusedWindow which was
            // controlling the system ui visibility. So if mLastFocusedWindow can still receive
            // keys, we let it keep controlling the visibility.
            final boolean lastFocusCanReceiveKeys =
                    (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys());
            winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade
                    : lastFocusCanReceiveKeys ? mLastFocusedWindow
                            : mTopFullscreenOpaqueWindowState;
            if (winCandidate == null) {
                return;
            }
        }
        final WindowState win = winCandidate;
        mSystemUiControllingWindow = win;

        final int displayId = getDisplayId();//获取屏幕设备id
        final int disableFlags = win.getDisableFlags();//获取窗口禁止的属性标记
        //结合参数disableFlags重新更新系统栏窗口参数
        final int opaqueAppearance = updateSystemBarsLw(win, disableFlags);

 		final WindowState navColorWin = chooseNavigationColorWindowLw(mNavBarColorWindowCandidate,
        mDisplayContent.mInputMethodWindow, mNavigationBarPosition);
        final boolean isNavbarColorManagedByIme =
                navColorWin != null && navColorWin == mDisplayContent.mInputMethodWindow;
        final int appearance = updateLightNavigationBarLw(win.mAttrs.insetsFlags.appearance,
                navColorWin) | opaqueAppearance;
        final int behavior = win.mAttrs.insetsFlags.behavior;
        final String focusedApp = win.mAttrs.packageName;
        final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR)
                || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR);
        final AppearanceRegion[] appearanceRegions =
                new AppearanceRegion[mStatusBarColorWindows.size()];
        for (int i = mStatusBarColorWindows.size() - 1; i >= 0; i--) {
            final WindowState windowState = mStatusBarColorWindows.get(i);
            appearanceRegions[i] = new AppearanceRegion(
                    getStatusBarAppearance(windowState, windowState),
                    new Rect(windowState.getFrame()));
        }
        if (mLastDisableFlags != disableFlags) {
            mLastDisableFlags = disableFlags;
            final String cause = win.toString();
            callStatusBarSafely(statusBar -> statusBar.setDisableFlags(displayId, disableFlags,
                    cause));
        }
        if (mLastAppearance == appearance
                && mLastBehavior == behavior
                && mRequestedVisibilities.equals(win.getRequestedVisibilities())
                && Objects.equals(mFocusedApp, focusedApp)
                && mLastFocusIsFullscreen == isFullscreen
                && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
            return;
        }
        if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
                && ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) {
            mService.mInputManager.setSystemUiLightsOut(
                    isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0);
        }
        final InsetsVisibilities requestedVisibilities =
                new InsetsVisibilities(win.getRequestedVisibilities());
        mLastAppearance = appearance;
        mLastBehavior = behavior;
        mRequestedVisibilities = requestedVisibilities;
        mFocusedApp = focusedApp;
        mLastFocusIsFullscreen = isFullscreen;
        mLastStatusBarAppearanceRegions = appearanceRegions;
        //调用状态栏管理服务StatusBarManagerService的onSystemBarAttributesChanged
        callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
                appearance, appearanceRegions, isNavbarColorManagedByIme, behavior,
                requestedVisibilities, focusedApp));
    }
 }

updateSystemBarAttributes方法首先获取当前的焦点窗口mFocusedWindow,将该窗口赋值给winCandidate,并判断该窗口是否可以操控系统栏,如果不允许会直接返回;如果允许,则会结合winCandidate的属性调用updateSystemBarsLw方法,来更新系统栏参数,之后还会调用状态栏管理服务StatusBarManagerService的onSystemBarAttributesChanged,而此回调方法最终会回传给系统状态栏和导航栏,使得新的SystemUIVisibility生效。文章来源地址https://www.toymoban.com/news/detail-737326.html

到了这里,关于Android 12系统源码_SystemUI(八)SystemUIVisibility属性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android 12.0 SystemUI控制系统手势左右滑返回功能

      在12.0的系统rom定制化产品开发中,在10.0以后系统默认手势中有三键导航和系统手势导航,在系统systemui设置默认系统手势导航以后,左右滑动手势返回功能 是在SystemUI中具体实现的,现在有需要要求控制左右滑动手势返回功能的启用和禁用,所以要分析手势返回功能的具体

    2024年02月03日
    浏览(35)
  • Android 10.0 SystemUI定制之通过系统属性控制锁屏页面通知栏显示与隐藏功能实现

    在10.0的系统产品开发中,在一些SystemUI的系统定制化开发中,在对锁屏页面的通知栏在某些情况下不需要显示通知栏,所以就需要 在systemui的通知栏布局页面中,通过属性来控制是否在锁屏页面的时候显示通知,具体就分析下systemui然后开发相关功能 在systemui系统中最主要的

    2024年02月04日
    浏览(34)
  • Android 12.0 系统systemui状态栏下拉左滑显示通知栏右滑显示控制中心模块的流程分析

      在android12.0的系统rom定制化开发中,在系统原生systemui进行自定义下拉状态栏布局的定制的时候,需要在systemui下拉状态栏下滑的时候,根据下滑坐标来 判断当前是滑出通知栏还是滑出控制中心模块,所以就需要根据屏幕宽度,来区分x坐标值为多少是左滑出通知栏或者右滑

    2024年02月09日
    浏览(41)
  • Android 10.0 系统systemui下拉通知栏的通知布局相关源码分析

     在android10.0的系统rom开发中,在进行systemui中的下拉通知栏的布局自定义的时候,对于原生systemui的 系统的下拉通知栏的通知布局的了解也是非常重要的,接下来就来分析下相关的下拉通知栏的通知布局的相关 源码流程,了解这些才方便对通知栏的布局做修改   在10.0的系统

    2023年04月21日
    浏览(33)
  • Android 12.0 系统settings系统属性控制一级菜单显示隐藏

    在12.0的系统rom定制化开发中,系统settings的一级菜单有些在客户需求中,要求通过系统属性来控制显示隐藏,从而达到控制一级菜单的显示的目的,而系统settings是通过静态加载的方式负责显示隐藏

    2024年02月06日
    浏览(70)
  • android 12.0SystemUI 状态栏下拉快捷添加截图快捷开关

    在12.0的系统产品rom定制化开发中,对SystemUI的定制需求也是挺多的,在下拉状态栏中 添加截图快捷开关,也是常有的开发功能,下面就以添加 截图功能为例功能的实现 在systemUI的res下的config中的quick_settings_tiles_default 和 quick_settings_tiles_stock是默认添加下拉快捷的字符资源,在

    2024年02月07日
    浏览(35)
  • Android 12 MTK SystemUI 下拉状态栏 UI定制化开发(1)

    1. 概述 距离上次更新有一周时间过去了,忙着工作,没来得及更新博客,各位看官老爷久等了,这次打算写一个UI定制系列,关于MTK  android 12  UI定制吧!   2. 效果图 原生效果图:  定制后效果图: 3.修改路径 4.总结 想要修改这块UI界面,需要梳理出整体流程是怎么过来,

    2024年02月13日
    浏览(30)
  • Android 12.0SystemUI 下拉状态栏默认展开下拉框(展开下拉快捷)

    在12.0定制化开发中,在进行定制SystemUI 下拉状态栏的时候 ,需要默认展开下拉框 显示出所以的下拉快捷图标 就是不需要二次展开下拉状态栏  这就要从NotificationPanelView.java中 下拉事件处理 而在12.0中下拉事件全都有NotificationPanelViewController.java 来处理了

    2024年02月06日
    浏览(86)
  • Android 12.0 原生SystemUI下拉通知栏UI背景设置为圆角背景的定制(二)

     在12.0的系统rom定制化开发中,在原生系统SystemUI下拉状态栏的下拉通知栏的背景默认是白色四角的背景, 由于在产品设计中,在对下拉通知栏通知的背景需要把四角背景默认改成圆角背景,所以就需要分析系统原生下拉通知栏的每条通知的默认背景, 然后通过systemui的通知

    2024年02月08日
    浏览(38)
  • Android 12.0 原生SystemUI下拉通知栏UI背景设置为圆角背景的定制(一)

     在12.0的系统rom定制化开发中,在原生系统SystemUI下拉状态栏的通知栏的背景是白色四角的背景,由于在产品设计中,需要把四角背景默认改成圆角背景,所以就需要分析系统原生下拉通知栏的每条通知的默认背景,然后通过systemui的通知流程,设置默认下拉状态栏UI中的通知

    2024年02月05日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包