该系列文章总纲链接:专题分纲目录 Android SystemUI组件
本章关键点总结 & 说明:
说明:本章节持续迭代之前章节的思维导图,主要关注右下方 SystemBars分析中状态栏的显示信息 和 创建部分即可。
1 状态栏简介
状态栏是一个将所有信息集中显示的场所,对需要显示的信息分成五大类:
- 通知信息:下拉时会显示更详细信息。使用者可以通过StatusBarManagerService所提供的接口向状态栏中添加或移除一条通知信息。
- 时间信息:在状态栏最右侧的一个小型数字时钟图标。
- 电量信息:在数字时钟左侧的一个电池图标,显示当前电量。
- 信号信息:在电量信息的左侧的一系列图标,用于显示系统当前Wifi、移动网络信号状态。Wifi图标、手机信号图标、飞行模式图标均为信号信息的范畴。
- 系统状态图标区:这个区域用一系列图标标识系统当前的状态,位于信号信息的左侧。与通知信息类似,StatusBarManagerService通过setIcon()接口为外界提供了修改系统状态图标区的图标的途径,而然它对信息的内容有很强的限制。详细描述如下:
- 系统状态图标区无法显示图标以外的信息。
- 系统状态图标区对其所显示的图标数量以及图标所表示的意图有着严格的限制。
2 状态栏创建之addStatusBarWindow入口分析
从上一章节我们对状态栏的分析截止到addStatusBarWindow,这里接续分析,代码实现如下:
private void addStatusBarWindow() {
makeStatusBarView();//创建statusbar控件树
mStatusBarWindowManager = new StatusBarWindowManager(mContext);
//将状态栏添加到window显示
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
在addStatusBarWindow()方法中,PhoneStatusBar 将会构建状态栏的控件树并通过WM的接口为其创建窗口。这里主要分析两个部分:makeStatusBarView创建statusbar控件树 和 将状态栏添加到WM(会详细分析StatusBarWindowManager的add函数如何调用WindowManager的add实现原理)上显示的过程。
2.1 状态栏Layout相关
makeStatusBarView主要是为了创建statusbar控件树,代码实现如下:
protected PhoneStatusBarView makeStatusBarView() {
final Context context = mContext;
Resources res = context.getResources();
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
//根布局设置
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
mStatusBarWindow.mService = this;
//mStatusBarWindow 触摸动画相关...
//状态栏布局
mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
mStatusBarView.setBar(this);
//下拉通知栏布局
PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
mStatusBarView.setPanelHolder(holder);
mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
R.id.notification_panel);
mNotificationPanel.setStatusBar(this);
//...
updateShowSearchHoldoff();
//...
//导航栏View处理相关
//...
// figure out which pixel-format to use for the status bar.
mPixelFormat = PixelFormat.OPAQUE;
//...
//布局相关设置...
//...
//...
//quick settings panel设置相关
//...
// User info. Trigger first load.
mHeader.setUserInfoController(mUserInfoController);
mKeyguardStatusBar.setUserInfoController(mUserInfoController);
mUserInfoController.reloadUserInfo();
mHeader.setBatteryController(mBatteryController);
((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(
mBatteryController);
mKeyguardStatusBar.setBatteryController(mBatteryController);
mHeader.setNextAlarmController(mNextAlarmController);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBroadcastReceiver.onReceive(mContext,
new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
//接收广播相关设置
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(ACTION_DEMO);
context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
// listen for USER_SETUP_COMPLETE setting (per-user)
resetUserSetupObserver();
startGlyphRasterizeHack();
return mStatusBarView;
}
这里分析xml布局文件:Super_status_bar.xml、Status_bar.xml、Status_bar_expanded.xml。
@1 布局文件Super_status_bar.xml简要解读
在创建控件树中,根布局文件为super_status_bar.xml,内容如下:
<!-- This is the combined status bar / notification panel window. -->
<com.android.systemui.statusbar.phone.StatusBarWindowView
xmlns:android="http://schemas.android.com/apk/res/android"
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"
>
<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" />
<include layout="@layout/status_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height" />
<FrameLayout android:id="@+id/brightness_mirror"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="wrap_content"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:paddingLeft="@dimen/notification_side_padding"
android:paddingRight="@dimen/notification_side_padding"
android:visibility="gone">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="2dp"
android:background="@drawable/brightness_mirror_background">
<include layout="@layout/quick_settings_brightness_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</FrameLayout>
</FrameLayout>
<com.android.systemui.statusbar.phone.PanelHolder
android:id="@+id/panel_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/transparent" >
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</com.android.systemui.statusbar.phone.PanelHolder>
<com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no" />
</com.android.systemui.statusbar.phone.StatusBarWindowView>
这里涉及以下布局和视图相关的概念:
- StatusBarWindowView:状态栏根布局
- BackDropView:背景视图
- status_bar:状态栏布局
- quick_setting_brightness_dialog:快速设定亮度对话框
- PanelHolder:下拉通知栏布局,是状态栏的卷帘。注意:PanelHolder在图标、时间之上,而且宽度占满屏幕
- status_bar_expand:卷帘的通知列表面板。
- ScrimView:状态栏下拉后,背景,半透明灰色。
@2 布局文件Status_bar.xml简要解读
状态栏布局文件为Status_bar.xml,内容如下:
<!--android:background="@drawable/status_bar_closed_default_background" -->
<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:id="@+id/status_bar"
android:background="@drawable/system_bar_background"
android:orientation="vertical"
android:focusable="true"
android:descendantFocusability="afterDescendants"
>
<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"
/>
<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"
>
<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"
>
<!-- The alpha of this area is both controlled from PhoneStatusBarTransitions and
PhoneStatusBar (DISABLE_NOTIFICATION_ICONS), so we need two views here. -->
<com.android.keyguard.AlphaOptimizedLinearLayout
android:id="@+id/notification_icon_area_inner"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.android.systemui.statusbar.StatusBarIconView android:id="@+id/moreIcon"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
android:src="@drawable/stat_notify_more"
android:visibility="gone"
/>
<com.android.systemui.statusbar.phone.IconMerger android:id="@+id/notificationIcons"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:gravity="center_vertical"
android:orientation="horizontal"/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<include layout="@layout/system_icons" />
<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="7dp"
android:gravity="center_vertical|start"
/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
<ViewStub
android:id="@+id/ticker_stub"
android:inflatedId="@+id/ticker"
android:layout="@layout/status_bar_ticker"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</com.android.systemui.statusbar.phone.PhoneStatusBarView>
这里涉及以下布局和视图相关的概念:
- notification_lights_out:低辨识度模式下,一般情况下不可见。
- status_bar_contents:状态栏上各种信息显示场所。
- ticker:当一个新通知到来时(比如微信),状态栏上会以动画方式逐行显示通知内容,使得用户可以在无需下拉卷帘的情况下了解新通知的内容,这一功能被称为ticker,一般情况下不可见。
@3 布局文件Status_bar_expanded.xml简要解读
卷帘的通知列表面板 布局文件为Status_bar_expanded.xml,内容如下:
<com.android.systemui.statusbar.phone.NotificationPanelView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/notification_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
>
<include
layout="@layout/carrier_label"
android:layout_height="@dimen/carrier_label_height"
android:layout_width="match_parent"
android:layout_marginBottom="@dimen/close_handle_height"
android:layout_gravity="bottom"
/>
<include
layout="@layout/keyguard_status_view"
android:layout_height="wrap_content"
android:visibility="gone" />
<TextView
android:id="@+id/emergency_calls_only"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network.EmergencyOnly"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="4dp"
android:gravity="center"
android:visibility="gone"
/>
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:id="@+id/notification_container_parent"
android:clipToPadding="false"
android:clipChildren="false">
<com.android.systemui.statusbar.phone.ObservableScrollView
android:id="@+id/scroll_view"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:scrollbars="none"
android:overScrollMode="never"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
layout="@layout/qs_panel"
android:layout_marginTop="@dimen/status_bar_header_height_expanded"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/notification_side_padding"
android:layout_marginRight="@dimen/notification_side_padding"/>
<!-- A view to reserve space for the collapsed stack -->
<!-- Layout height: notification_min_height + bottom_stack_peek_amount -->
<View
android:id="@+id/reserve_notification_space"
android:layout_height="@dimen/min_stack_height"
android:layout_width="match_parent"
android:layout_marginTop="@dimen/notifications_top_padding" />
<View
android:layout_height="@dimen/notification_side_padding"
android:layout_width="match_parent" />
</LinearLayout>
</com.android.systemui.statusbar.phone.ObservableScrollView>
<com.android.systemui.statusbar.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:layout_marginBottom="@dimen/close_handle_underlap"
android:importantForAccessibility="no" />
<ViewStub
android:id="@+id/keyguard_user_switcher"
android:layout="@layout/keyguard_user_switcher"
android:layout_height="match_parent"
android:layout_width="match_parent" />
<include
layout="@layout/keyguard_status_bar"
android:visibility="invisible" />
</com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
<include
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
<include layout="@layout/status_bar_expanded_header" />
<com.android.systemui.statusbar.AlphaOptimizedView
android:id="@+id/qs_navbar_scrim"
android:layout_height="96dp"
android:layout_width="match_parent"
android:layout_gravity="bottom"
android:visibility="invisible"
android:background="@drawable/qs_navbar_scrim" />
</com.android.systemui.statusbar.phone.NotificationPanelView>
这里涉及以下布局和视图相关的概念:
- carrier_label:显示运营商标签
- keyguard_status_view:锁屏UI的时钟信息,将keyguard_status_area 布局包含在这里面
- emergency_calls_only:紧急电话TextView
- notification_container_parent:NotificationsQuickSettingsContainer下拉通知栏布局容器
- ObservableScrollView:scroll_view子类,滚动视图,包括快速设置按钮布局、
- reserve_notification_space:通知信息分割线
- NotificationStackScrollLayout:可滚动的通知栏布局
最后 这里总结下 这几个关键布局以及关键视图之间的关系(关键部分,非全部),如下所示:
2.2 将状态栏添加到WM
这里主要分析如下关键语句:
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
继续分析add方法,代码实现如下:
public void add(View statusBarView, int barHeight) {
/*
FLAG_NOT_FOCUSABLE:状态栏不接受按键事件
FLAG_TOUCHABLE_WHEN_WAKING:标志使得状态栏接受导致设备唤醒的触摸事件。通常这一事件会被用于唤醒设备(或从变暗状态下恢复)
FLAG_SPLIT_TOUCH:允许状态栏支持触摸事件序列的拆分
FLAG_WATCH_OUTSIDE_TOUCH:接收view以外的触摸事件
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS:实现沉浸式状态栏,系统bar的background绘制,绘制透明背景的系统bar(状态栏和导航栏)
*/
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
barHeight, //高度来自于getStatusBarHeight()方法
WindowManager.LayoutParams.TYPE_STATUS_BAR, // 窗口类型
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
//设置标志位,启用硬件加速
mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
mLp.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mStatusBarView = statusBarView;
mBarHeight = barHeight;
//通过WindowManager.addView()创建状态栏的窗口
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
}
总体来说,该部分主要是创建WindowManager.LayoutParams并对其进行属性赋值,最后和StatusBarView一起传递给WM的addView方法。