该系列文章总纲链接:专题分纲目录 Android SystemUI组件
本章关键点总结 & 说明:
说明:本章节持续迭代之前章节思维导图,主要关注左侧最上方SystemUiVisibility解读部分即可。
本章节主要讲解SystemUiVisibility的概念及其相关常用的属性,以及在应用中如何使用,最后研究下在framework层setSystemUiVisibility的具体实现逻辑及涉及到的一些相关内容。
1 理解SystemUIVisibility
1.1 SysIVisibility 简介
在Android系统中,SystemUIVisibility 是普通应用用于控制系统UI元素(如状态栏和导航栏)可见性的机制。通过设置不同的标志,开发者可以控制这些UI元素的显示和隐藏,以及它们对应用布局的影响。以下是一些与SystemUIVisibility相关的常用属性:
- SYSTEM_UI_FLAG_LAYOUT_STABLE:当系统栏的可见性改变时,保持应用的布局稳定,避免内容布局随着系统栏的显示和隐藏而跳动。
- SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION:允许应用的布局扩展到导航栏区域,即使导航栏可见。
- SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:允许应用的布局扩展到状态栏区域,即使状态栏可见。
- SYSTEM_UI_FLAG_HIDE_NAVIGATION:隐藏导航栏,但用户可以通过滑动屏幕边缘来重新显示导航栏。
- SYSTEM_UI_FLAG_FULLSCREEN:隐藏状态栏,但用户可以通过下拉屏幕顶部来重新显示状态栏。
- SYSTEM_UI_FLAG_IMMERSIVE:提供一种沉浸式体验,系统栏不会自动显示,直到用户执行特定的滑动操作。
- SYSTEM_UI_FLAG_IMMERSIVE_STICKY:类似于SYSTEM_UI_FLAG_IMMERSIVE,但系统栏会在一定时间后自动隐藏。
- SYSTEM_UI_FLAG_LIGHT_STATUS_BAR:将状态栏的文字和图标颜色设置为深色,以便在浅色背景上清晰可见。
使用setSystemUiVisibility方法时,可以通过按位或操作(|)组合多个标志来实现复杂的系统UI控制。
1.2 解读应用中setSystemUiVisibility方法的使用
在Android中,setSystemUiVisibility(int visibility)方法是View类的一部分,通常在Activity的某个视图上调用,以控制系统UI元素(如状态栏和导航栏)的可见性。以下是一个普通应用中如何使用setSystemUiVisibility方法的步骤:
- 获取布局中的视图: 首先,你需要获取到Activity主布局或者特定的视图,这取决于你想要影响的UI部分。
- 调用setSystemUiVisibility方法: 在视图上调用setSystemUiVisibility方法,并传入一个或多个标志的组合,这些标志定义了系统UI的可见性。
- 处理onWindowFocusChanged回调: 在你的Activity中重写onWindowFocusChanged方法,并在其中调用setSystemUiVisibility方法。当窗口焦点发生变化时,这个方法会被调用。
下面是一个示例代码,展示了如何在Activity中隐藏状态栏:
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// 隐藏状态栏
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // 隐藏导航栏
| View.SYSTEM_UI_FLAG_FULLSCREEN // 隐藏状态栏
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
}
在这个例子中,当Activity获得窗口焦点时,状态栏和导航栏会被隐藏,并且布局会扩展到这些UI元素的区域。
注意:setSystemUiVisibility
方法在API级别11中引入,在31中废除,所以如果你的应用支持的最低API级别低于11,你需要做兼容性处理。
使用setSystemUiVisibility
是控制应用内系统UI元素可见性的简单有效方式。
2 setSystemUiVisibility流程解读(framework层分析)
2.1 从View的setSystemUiVisibility方法开始解读
接下来开始分析setSystemUiVisibility的实现,对应的代码实现如下所示:
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
private static final boolean DBG = false;
//...
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
mSystemUiVisibility = visibility;
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);
}
}
}
//...
}
过程中除了调用View的可能会调用ViewGroup中的recomputeViewAttributes方法,对应的代码实现如下所示:
//ViewGroup
public void recomputeViewAttributes(View child) {
if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
ViewParent parent = mParent;
if (parent != null) parent.recomputeViewAttributes(this);
}
}
但是不管怎样,最后一定会调用到ViewRootImpl的recomputeViewAttributes方法,对应的代码实现如下:
//ViewRootImpl
//...
//关键流程 step1
@Override
public void recomputeViewAttributes(View child) {
checkThread(); // 确保该方法在UI线程中调用
if (mView == child) {
mAttachInfo.mRecomputeGlobalAttributes = true; // 设置标志,表示需要重新计算全局属性
if (!mWillDrawSoon) { // 如果当前没有即将进行的绘制操作
scheduleTraversals(); // 调度绘制流程,以便更新视图
}
}
}
//...
//关键流程 step2
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
//等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}
//...
//关键流程 step3
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
//...
//关键流程 step4
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
try {
//执行performTraversals
performTraversals();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
//...
//关键流程 step5
p