StatusBar源码解读

背景

​ 状态栏属于SystemUI的管理范畴,虽然app界面的UI会受到SystemUI的影响,但是app并没有权限去直接绘制SystemUI的权限.所以还是需要通过更改一些SystemUI暴露出来的方法进行属性的修改,然后最终操纵的还是自己的View,来达到预期的效果

结构

StatusBarWindowView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- StatusBar的最外层-->

<com.android.systemui.statusbar.phone.StatusBarWindowView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:focusable="true"
android:descendantFocusability="afterDescendants"
android:fitsSystemWindows="true"
android:background="@android:color/transparent"
>
<!--状态栏通常的显示状态-->
<include layout="@layout/status_bar"
android:layout_width="match_parent"
android:layout_height="@*android:dimen/status_bar_height"
/>

<!--状态栏下拉展开状态-->
<com.android.systemui.statusbar.phone.PanelHolder
android:id="@+id/panel_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<ViewStub android:id="@+id/quick_settings_stub"
android:layout="@layout/quick_settings"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</com.android.systemui.statusbar.phone.PanelHolder>

</com.android.systemui.statusbar.phone.StatusBarWindowView>

PhoneStatusBarView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<!--对应@layout/status_bar,是StatusBar的核心功能展示部分-->

<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"
android:fitsSystemWindows="true"
>

<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"
/>

<!-- 通常StatusBar的显示样式-->
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="6dip"
android:paddingEnd="6dip"
android:orientation="horizontal"
>

<LinearLayout
android:id="@+id/notification_icon_area"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
>
<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"/>
</LinearLayout>

<LinearLayout android:id="@+id/system_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">

<LinearLayout android:id="@+id/statusIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal"/>

<LinearLayout
android:id="@+id/signal_battery_cluster"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingStart="2dp"
android:orientation="horizontal"
android:gravity="center"
>
<include layout="@layout/signal_cluster_view"
android:id="@+id/signal_cluster"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<!-- battery must be padded below to match assets -->
<com.android.systemui.BatteryMeterView
android:id="@+id/battery"
android:layout_height="16dp"
android:layout_width="10.5dp"
android:layout_marginBottom="0.33dp"
android:layout_marginStart="4dip"
/>
</LinearLayout>

<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="6dip"
android:gravity="center_vertical|start"
/>
</LinearLayout>
</LinearLayout>

<!--当有notification到达的时候显示的样式-->
<LinearLayout android:id="@+id/ticker"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="6dip"
android:animationCache="false"
android:orientation="horizontal" >
<ImageSwitcher android:id="@+id/tickerIcon"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="@dimen/status_bar_icon_size"
android:layout_marginEnd="4dip"
>
<com.android.systemui.statusbar.AnimatedImageView
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="@dimen/status_bar_icon_size"
android:scaleType="center"
/>
<com.android.systemui.statusbar.AnimatedImageView
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="@dimen/status_bar_icon_size"
android:scaleType="center"
/>
</ImageSwitcher>
<com.android.systemui.statusbar.phone.TickerView android:id="@+id/tickerText"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
android:paddingTop="2dip"
android:paddingEnd="10dip">
<TextView
android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
<TextView
android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
</com.android.systemui.statusbar.phone.TickerView>
</LinearLayout>
</com.android.systemui.statusbar.phone.PhoneStatusBarView>

PhoneStatusBar

1
2
3
4
5
6
//as a View Controller
public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
HeadsUpManager.OnHeadsUpChangedListener {
//......
}

沉浸式属性

android:windowTranslucentStatus

  • 目的:设置SystemUI中的statusBar的背景色为透明
  • ApiLevel: Android 4.4/ApiLevel 19

android:windowTranslucentNavigation

  • 目的:设置SystemUI中的Navigation背景为透明
  • ApiLevel:Android 4.4/ApiLevel 19

android:statusBarColor

  • 目的:暴露接口设置statueBar的颜色
  • ApiLevel:Android 5.x/ApiLevel 21

android:fitsSystemWindows=”true”

  • 目的:和systemUI和谐共处,所以不会占用systemUI空间,用上下左右的padding值进行填充,以保持和SystemUi和谐共处.

android:clipToPadding=”true”

  • 目的:和padding值交集的部分是否采用clip的显示方式

显示流程

时序图

通过Zygote启动SystemServer最终启动SystemUIServer

StatusBar_Init

相关代码

BaseStatusBar.start()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/**
* 1.register systerm bar manager service
* 2.createAndAddWindows() install views/ PhoneStatusBar makeStatusBarView
* 3.addIcon List
* 4.register notifcation
* 5.register broadcast
*
*/
public void start() {
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
mDisplay = mWindowManager.getDefaultDisplay();
mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
//step 1
mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);

mNotificationData = new NotificationData(this);

mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);

mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.checkService(DreamService.DREAM_SERVICE));
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);

mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
mSettingsObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
mSettingsObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
mSettingsObserver,
UserHandle.USER_ALL);

mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
true,
mLockscreenSettingsObserver,
UserHandle.USER_ALL);

mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));

mRecents = getComponent(Recents.class);
mRecents.setCallback(this);

final Configuration currentConfig = mContext.getResources().getConfiguration();
mLocale = currentConfig.locale;
mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
mFontScale = currentConfig.fontScale;

mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);

mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.linear_out_slow_in);
mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.fast_out_linear_in);

// Connect in to the status bar manager service
StatusBarIconList iconList = new StatusBarIconList();
mCommandQueue = new CommandQueue(this, iconList);

int[] switches = new int[8];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
try {
mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
//step 2
createAndAddWindows();

mSettingsObserver.onChange(false); // set up
disable(switches[0], switches[6], false /* animate */);
setSystemUiVisibility(switches[1], 0xffffffff);
topAppWindowChanged(switches[2] != 0);
// StatusBarManagerService has a back up of IME token and it's restored here.
setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
//step 3
// Set up the initial icon state
int N = iconList.size();
int viewIndex = 0;
for (int i=0; i<N; i++) {
StatusBarIcon icon = iconList.getIcon(i);
if (icon != null) {
addIcon(iconList.getSlot(i), i, viewIndex, icon);
viewIndex++;
}
}
//step 4
// Set up the initial notification state.
try {
mNotificationListener.registerAsSystemService(mContext,
new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
UserHandle.USER_ALL);
} catch (RemoteException e) {
Log.e(TAG, "Unable to register notification listener", e);
}


if (DEBUG) {
Log.d(TAG, String.format(
"init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
iconList.size(),
switches[0],
switches[1],
switches[2],
switches[3]
));
}

mCurrentUserId = ActivityManager.getCurrentUser();
setHeadsUpUser(mCurrentUserId);
//step 5
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(BANNER_ACTION_CANCEL);
filter.addAction(BANNER_ACTION_SETUP);
mContext.registerReceiver(mBroadcastReceiver, filter);

IntentFilter allUsersFilter = new IntentFilter();
allUsersFilter.addAction(
DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
null, null);
updateCurrentProfilesCache();
}

开源框架(SystemBarTint)

一个工具式的开源框架

initBar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
1.make systemUI transparent
2.add View own at the same place and then you can control the performance of your own View
**/
public SystemBarTintManager(Activity activity) {

Window win = activity.getWindow();
ViewGroup decorViewGroup = (ViewGroup) win.getDecorView();

//android 4.4
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// check theme attrs
int[] attrs = {android.R.attr.windowTranslucentStatus,
android.R.attr.windowTranslucentNavigation};
TypedArray a = activity.obtainStyledAttributes(attrs);
try {
mStatusBarAvailable = a.getBoolean(0, false);
mNavBarAvailable = a.getBoolean(1, false);
} finally {
a.recycle();
}

// check window flags
WindowManager.LayoutParams winParams = win.getAttributes();
int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
if ((winParams.flags & bits) != 0) {
mStatusBarAvailable = true;
}
bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
if ((winParams.flags & bits) != 0) {
mNavBarAvailable = true;
}
}

mConfig = new SystemBarConfig(activity, mStatusBarAvailable, mNavBarAvailable);
// device might not have virtual navigation keys
if (!mConfig.hasNavigtionBar()) {
mNavBarAvailable = false;
}

if (mStatusBarAvailable) {
setupStatusBarView(activity, decorViewGroup);
}
if (mNavBarAvailable) {
setupNavBarView(activity, decorViewGroup);
}

}

SystemBarConfig

Class which describes system bar sizing and other characteristics for the current device configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public static class SystemBarConfig {
//......

private final boolean mTranslucentStatusBar;
private final boolean mTranslucentNavBar;
private final int mStatusBarHeight;
private final int mActionBarHeight;
private final boolean mHasNavigationBar;
private final int mNavigationBarHeight;
private final int mNavigationBarWidth;
private final boolean mInPortrait;
private final float mSmallestWidthDp;
//......
}

setupStatusBarView&&setStatusBarAlpha

1
2
3
4
5
6
7
8
9
10
11
12
private void setupStatusBarView(Context context, ViewGroup decorViewGroup) {
mStatusBarTintView = new View(context);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getStatusBarHeight());
params.gravity = Gravity.TOP;
if (mNavBarAvailable && !mConfig.isNavigationAtBottom()) {
params.rightMargin = mConfig.getNavigationBarWidth();
}
mStatusBarTintView.setLayoutParams(params);
mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);
mStatusBarTintView.setVisibility(View.GONE);
decorViewGroup.addView(mStatusBarTintView);
}
1
2
3
4
5
6
7
8
9
10
11
/**
* Apply the specified alpha to the system status bar.
*
* @param alpha The alpha to use
*/
@TargetApi(11)
public void setStatusBarAlpha(float alpha) {
if (mStatusBarAvailable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mStatusBarTintView.setAlpha(alpha);
}
}