在上一篇(https://blog.csdn.net/u011164827/article/details/102998091)分析到SystemUI的启动过程,现在分析StatusBar。
启动分析
SystemUI在SystemUIApplication会启动各个模块,在这个地方会调用com.android.systemui.SystemBars的start方法。
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
@Override
40 public void start() {
41 if (DEBUG) Log.d(TAG, "start");
42 createStatusBarFromConfig();
43 }
private void createStatusBarFromConfig() {
53 if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
//取出className
54 final String clsName = mContext.getString(R.string.config_statusBarComponent);
55 if (clsName == null || clsName.length() == 0) {
56 throw andLog("No status bar component configured", null);
57 }
// 通过反射获取该对象
58 Class<?> cls = null;
59 try {
60 cls = mContext.getClassLoader().loadClass(clsName);
61 } catch (Throwable t) {
62 throw andLog("Error loading status bar component: " + clsName, t);
63 }
64 try {
65 mStatusBar = (SystemUI) cls.newInstance();
66 } catch (Throwable t) {
67 throw andLog("Error creating status bar component: " + clsName, t);
68 }
//填充信息并启动 StatusBar start() 方法
69 mStatusBar.mContext = mContext;
70 mStatusBar.mComponents = mComponents;
71 mStatusBar.start();
72 if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
73 }
这里面的代码不多,主要是通过反射获取config_statusBarComponent
中定义的对象,并启动该对象的start方法。config_statusBarComponent
的值有3种,默认是phone布局,另外两个是tv和car。
44 <!-- Component to be used as the status bar service. Must implement the IStatusBar
45 interface. This name is in the ComponentName flattened format (package/class) -->
46 <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
我们这里分析frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public void start() {
//由于状态栏的窗口不属于任何一个Activity,所以需要使用WindowManager窗口创建
656 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
657
658 mDisplay = mWindowManager.getDefaultDisplay();
659 updateDisplaySize();
660
661 Resources res = mContext.getResources();
662 mVibrateOnOpening = mContext.getResources().getBoolean(
663 R.bool.config_vibrateOnIconAnimation);
664 mVibratorHelper = Dependency.get(VibratorHelper.class);
665 mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);
666 mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);
667
668 DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
669 putComponent(StatusBar.class, this);
670
671 // start old BaseStatusBar.start().
//状态栏的存在对窗口布局有重要的影响,因此状态栏中所发生的变化有必要通知给WMS
672 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
673 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
674 Context.DEVICE_POLICY_SERVICE);
675
676 mAccessibilityManager = (AccessibilityManager)
677 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
678
679 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
680
681 mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
682
683 mBarService = IStatusBarService.Stub.asInterface(
684 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
685
686 mRecents = getComponent(Recents.class);
687
688 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
689 mLockPatternUtils = new LockPatternUtils(mContext);
690
691 mMediaManager.setUpWithPresenter(this, mEntryManager);
692
693 // Connect in to the status bar manager service
//mCommandQueue是CommandQueue类的一个实例。CommandQueue继承自IStatusBar.Stub。因此它是IStatusBar的Bn端。在完成注册后,这一Binder对象的Bp端将会保存在
//IStatusBarService中,因此它是IStatusBarService与BaseStatusBar进行通信的桥梁。
694 mCommandQueue = getComponent(CommandQueue.class);
695 mCommandQueue.addCallbacks(this);
696 /*
*switches存储了一些杂项:禁用功能列表,SystemUIVisiblity,是否在导航栏中显示虚拟的菜单键,输入法窗口是否可见,输入法是否消费BACK键,是否接入了实体键盘
*实体键盘是否被启用
*/
697 int[] switches = new int[9];
698 ArrayList<IBinder> binders = new ArrayList<>();
699 ArrayList<String> iconSlots = new ArrayList<>();
700 ArrayList<StatusBarIcon> icons = new ArrayList<>();
701 Rect fullscreenStackBounds = new Rect();
702 Rect dockedStackBounds = new Rect();
703 try {
//向IStatusBarServie进行注册,并获取所有保存在IStatusBarService中的信息副本
704 mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
705 fullscreenStackBounds, dockedStackBounds);
706 } catch (RemoteException ex) {
707 // If the system process isn't there we're doomed anyway.
708 }
709 //创建状态栏与导航栏的窗口
710 createAndAddWindows();
711
712 // Make sure we always have the most current wallpaper info.
713 IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
714 mContext.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter);
715 mWallpaperChangedReceiver.onReceive(mContext, null);
716
717 mLockscreenUserManager.setUpWithPresenter(this, mEntryManager);
718 mCommandQueue.disable(switches[0], switches[6], false /* animate */); //禁用某些功能
719 setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
720 fullscreenStackBounds, dockedStackBounds); //设置SystemUIVisibilty
721 topAppWindowChanged(switches[2] != 0); //设置菜单键的可见性
722 // StatusBarManagerService has a back up of IME token and it's restored here.
723 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0); //根据输入法窗口的可见性调整导航栏的样式
724
725 // Set up the initial icon state
//依次向系统状态去添加状态图标
726 int N = iconSlots.size();
727 for (int i=0; i < N; i++) {
728 mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));
729 }
730
731 // Set up the initial notification state.
//初始化通知栏
732 mNotificationListener.setUpWithPresenter(this, mEntryManager);
733
734 if (DEBUG) {
735 Log.d(TAG, String.format(
736 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
737 icons.size(),
738 switches[0],
739 switches[1],
740 switches[2],
741 switches[3]
742 ));
743 }
744
745 setHeadsUpUser(mLockscreenUserManager.getCurrentUserId());
746
747 IntentFilter internalFilter = new IntentFilter();
748 internalFilter.addAction(BANNER_ACTION_CANCEL);
749 internalFilter.addAction(BANNER_ACTION_SETUP);
750 mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
751 null);
752
753 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
754 Context.VR_SERVICE));
755 try {
756 vrManager.registerListener(mVrStateCallbacks);
757 } catch (RemoteException e) {
758 Slog.e(TAG, "Failed to register VR mode state listener: " + e);
759 }
760
761 IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
762 ServiceManager.getService(Context.WALLPAPER_SERVICE));
763 try {
764 wallpaperManager.setInAmbientMode(false /* ambientMode */, false /* animated */);
765 } catch (RemoteException e) {
766 // Just pass, nothing critical.
767 }
768
769 // end old BaseStatusBar.start().
770
771 // Lastly, call to the icon policy to install/update all the icons.
//最后,调用图标策略以安装/更新所有图标
772 mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
773 mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
774
775 mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
776 mUnlockMethodCache.addListener(this);
777 startKeyguard();
778
779 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
780 putComponent(DozeHost.class, mDozeServiceHost);
781
782 mScreenPinningRequest = new ScreenPinningRequest(mContext);
783 mFalsingManager = FalsingManager.getInstance(mContext);
784
785 Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
786
787 Dependency.get(ConfigurationController.class).addCallback(this);
788 }
StatusBar.java中有大量的代码。
mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE));
这一行获取IStatusBarService的实例。IStatusBarService是一个系统服务,由ServerThread启动并常驻system_seerver进程中。IStatusBarService为那些对状态栏感兴趣的其它系统服务定义了一系列API,然而对SystemUI而言,它更像一个客户端。因为IStatusBarService会将操作状态栏的请求发给SystemUI,并由后者完成请求。
这里主要看一下createAndAddWindows方法
状态栏的布局
状态栏需要显示的信息分为以下5种:
- 通知信息:在状态栏左侧显示一个图标提醒用户,并在下拉卷帘中为用户显示更加详细的信息。
- 时间信息:显示在状态栏最右侧的一般小型数字时钟,是一个名为Clock的继承自TextView的控件。监听了几个和时间有关的广播:ACTION_TIME_TICK、ACTION_TIME_CHANGED、ACTION_TIMEZONE_CHANGED、ACTION_CONFIGURAITON_CHANGED。当其中一个广播到来时从Calendar类中获取当前的系统时间,然后进行字符串格式化后显示出来。时间信息的维护工作在状态栏内部完成,外界无法通过API修改时间信息的显示或行为。
- 电量信息:显示一个电池图标,用于提示设备当前的电量情况。它是一个被BatteryController类所管理的ImageView。BatteryController通过监听android.intent.action.ABTTERY_CHANGED广播以从BatteryService中获取电量信息。和时间信息一样,外界无法干预状态栏对电量信息的显示行为。
- 信号信息:显示系统当前的WiFi、移动信号网络的信号状态。用户所看到的WiFi图标、手机信号图标、飞行模式图标都属于信号信息的范围。它们被NetworkController类维护着。NetworkController监听了一系列与信号相关的广播,如WIFI_STATE_CHANGED_ACTION 、WIFI_STATE_SIM_ACTION、ACTION_AIRPLANE_MODE_CHANGED等,并在这些广播到来时显示、更改或移除相关的图标。注意,在Android 8.0之后,手机信号图标不在通过ImageView而是通过自定义view实现。
系统状态图标区:这个区域用来显示系统当前的状态,比如可以展示蓝牙状态、闹铃等。StatusBarManagerService通过setIcon()接口为外界提供了修改系统状态图标的途径,但是它对信息的内容有很强的限制。1、系统状态图标无法显示图标以外的信息;2、系统状态图标对其显示的图标数量以及图标锁表示的意图有严格的限制。
我们继续分析createAndAddWindows
public void createAndAddWindows() {
addStatusBarWindow();
}
private void addStatusBarWindow() {
//创建状态栏的控件树
makeStatusBarView();
mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
mRemoteInputController = new RemoteInputController(mHeadsUpManager);
//通过StatusBarWindowManager.add创建状态栏的窗口
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
/*
*获取statusbar高度,在framework/base/core/res/res/values/diamens.xml中设置
*/
public int getStatusBarHeight() {
if (mNaturalBarHeight < 0) {
final Resources res = mContext.getResources();
mNaturalBarHeight =
res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
}
return mNaturalBarHeight;
}
// ================================================================================
// Constructing the view
// ================================================================================
protected void makeStatusBarView() {
final Context context = mContext;
//获取屏幕参数
updateDisplaySize(); // populates mDisplayMetrics
//更新Panels资源数据,statusbar包含很多panel,在创建PhoneStatusBarView时需要更新panel数据
updateResources();
updateTheme();
inflateStatusBarWindow(context); //加载布局
mStatusBarWindow.setService(this);
mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener()); //mStatusBarWindow的点击事件
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG) //替换为CollapsedStatusBarFragment
.commit();
mIconController = Dependency.get(StatusBarIconController.class);
}
/*
*加载布局
*/
protected void inflateStatusBarWindow(Context context) {
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
}
调用流程是createAndAddWindows——>addStatusBarWindow——>makeStatusBarView
/**
87 * Adds the status bar view to the window manager.
88 *
89 * @param statusBarView The view to add.
90 * @param barHeight The height of the status bar in collapsed state.
91 */
92 public void add(View statusBarView, int barHeight) {
93
94 // Now that the status bar window encompasses the sliding panel and its
95 // translucent backdrop, the entire thing is made TRANSLUCENT and is
96 // hardware-accelerated.
97 mLp = new WindowManager.LayoutParams(
98 ViewGroup.LayoutParams.MATCH_PARENT,
99 barHeight,
100 WindowManager.LayoutParams.TYPE_STATUS_BAR,
101 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
102 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
103 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
104 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
105 | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
106 PixelFormat.TRANSLUCENT);
107 mLp.token = new Binder();
108 mLp.gravity = Gravity.TOP;
109 mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
110 mLp.setTitle("StatusBar");
111 mLp.packageName = mContext.getPackageName();
112 mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
113 mStatusBarView = statusBarView;
114 mBarHeight = barHeight;
115 mWindowManager.addView(mStatusBarView, mLp);
116 mLpChanged = new WindowManager.LayoutParams();
117 mLpChanged.copyFrom(mLp);
118 }
状态栏的高度是从frameworks/base/core/res/res/values/dimens.xml中获取的,默认为25dp。TYPE_STATUS_BAR使得PhomeWindowManager
为状态栏的窗口分配了较大的layer值,使其可以显示在其它应用窗口上。FLAG_NOT_FOCUSABLE、FLAG_TOUCHABLE_WHEN_WAKING、FLAG_SPLIT_TOUCH
定义了输入事件的响应行为。另外当窗口创建后LayoutParams是会反生变化的。状态栏窗口创建时高度为25dip,flags描述为其不可接受按键事件。不过当用户
按下状态栏导致卷帘下拉时,StatusBar会通过WindowManager.updateViewLayout()方法修改窗口的LayoutParams高度为match_parent,即充满整个屏幕使得卷帘
可以满屏显示,并且移除FLAG_NOT_FOCUSABLE,使得StatusBar可以监听back按钮
在inflateStatusBarWindow会初始化布局
<!-- This is the combined status bar / notification panel window. -->
<-- StatusBarWindowView 继承于FrameLayout -->
<com.android.systemui.statusbar.phone.StatusBarWindowView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.android.systemui.statusbar.BackDropView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
sysui:ignoreRightInset="true"
>
<ImageView android:id="@+id/backdrop_back"
android:layout_width="match_parent"
android:scaleType="centerCrop"
android:layout_height="match_parent" />
<ImageView android:id="@+id/backdrop_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="invisible" />
</com.android.systemui.statusbar.BackDropView>
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
<com.android.systemui.statusbar.AlphaOptimizedView
android:id="@+id/heads_up_scrim"
android:layout_width="match_parent"
android:layout_height="@dimen/heads_up_scrim_height"
android:background="@drawable/heads_up_scrim"
sysui:ignoreRightInset="true"
android:importantForAccessibility="no"/>
<FrameLayout
android:id="@+id/status_bar_container" //顶部状态栏
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/brightness_mirror" /> //亮度调节
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout="@layout/car_fullscreen_user_switcher"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/status_bar_expanded" //QuickSettings和Notification栏
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
在super_status_bar.xml文件中主要定义了三个布局顶部状态栏、亮度调节、QuickSettings和Notification。继续分析顶部状态栏,顶部状态栏是一个Fragment,布局文件是SystemUI/res/layout/status_bar.xml
<com.android.systemui.statusbar.phone.PhoneStatusBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height"
android:id="@+id/status_bar"
android:background="@drawable/system_bar_background"
android:orientation="vertical"
android:focusable="false"
android:descendantFocusability="afterDescendants"
>
<!--notification_lights_out 一般情况下是不可见,在SystemUIVisiblity中有一个名为SYSTEM_UI_FLAG_LOW_PROFLE的标记
当一个应用程序希望让客户注意力更对集中在它所显示的内容时,可以在其SystemUIVisiblity中添加这一标记,SYSTEM_UI_FLAG_LOW_PROFLE会使
得状态栏与导航栏进入低识别度模式。低识别度模式下的状态栏将不会显示任何信息,只是在黑色背景中显示一个灰色圆点,这个圆点即这个-->
<ImageView
android:id="@+id/notification_lights_out"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
android:paddingStart="6dip"
android:paddingBottom="2dip"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:visibility="gone"
/>
<!-- status_bar_contents显示顶部状态栏的各种信息 -->
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="6dp"
android:paddingEnd="8dp"
android:orientation="horizontal"
>
<!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
<!--消息通知 -->
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal" />
<!--system_icon_area 继承一个LinearLayout 除消息通知以外的信息 -->
<com.android.keyguard.AlphaOptimizedLinearLayout
android:id="@+id/system_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<!--显示wifi SIM卡 电量等 -->
<include layout="@layout/system_icons" />
!--clock 显示时间信息 -->
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:singleLine="true"
android:paddingStart="@dimen/status_bar_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_clock_end_padding"
android:gravity="center_vertical|start"
/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
<ViewStub
android:id="@+id/emergency_cryptkeeper_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/emergency_cryptkeeper_text"
/>
</com.android.systemui.statusbar.phone.PhoneStatusBarView>
@+id/system_icon_area的宽度定义为ware_content,而@+id/notification_icon_area的weight被设置为1.在这种情况下system_icon_area将在状态栏右侧根据其所显示的图标个数调整其尺寸。而notification_icon_area则会占用状态栏左侧的剩余空间。这说明一个问题:系统图标区将优先占用状态栏的控件进行信息的显示。
面又嵌套了一个SystemUI/res/layout/system_icons.xml布局,system_icons里面包含电量、系统状态、信号等图标。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_icons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical">
<!--系统状态图标 -->
<com.android.keyguard.AlphaOptimizedLinearLayout
android:id="@+id/statusIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"/>
<!--信号图标区域 -->
<include layout="@layout/signal_cluster_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
<!--显示电量信息 -->
<com.android.systemui.BatteryMeterView
android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
/>
</LinearLayout>
信号图标显示在signal_cluster_view.xml文件中,这里就不继续深入分析,等到分析信号时再做研究。
现在看一下状态栏的控件树结构
SystemUI常见问题修改
1、删除顶部状态栏
framework/base/core/res/res/values/dimens.xml中将status_bar_height设置为0dp
2、删除下方的导航栏
将qemu.hw.mainkeys属性设置为0
在看一下SystemUI的全套布局
参考资料:
Android 8.0 SystemUI(三):一说顶部 StatusBar
深入理解Android卷3