下拉后状态栏布局文件路径:SystemUI/res/layout/quick_qs_status_icons.xml
锁屏状态栏文件布局:SystemUI/res/layout/keyguard_status_bar.xml
一、状态栏布局
在没下拉状态栏时,原生android会将状态栏分为两部分(如下图及布局源码)
1、android:id=“@+id/notification_icon_area
2、android:id=”@+id/system_icon_area"
从下面的布局文件可以看到这两块区域的权重都是1,左边放通知图标,右边放系统图标,中间留有一块控件用来放缺口(也就是水滴或刘海的位置),在这里如果没有水滴的话可以修改权重,来放置更多的系统图标或通知图标(有水滴的话改了图标过多时就会遮挡住图标)
<FrameLayout
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1">
<include layout="@layout/heads_up_status_bar_layout" />
<!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
DISABLE_NOTIFICATION_ICONS, respectively -->
<LinearLayout
android:id="@+id/status_bar_left_side"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:clipChildren="false"
>
<ViewStub
android:id="@+id/operator_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/operator_name" />
<com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons_left"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingEnd="@dimen/signal_cluster_battery_padding"
android:gravity="center_vertical"
android:orientation="horizontal"/>
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:clipChildren="false"/>
</LinearLayout>
</FrameLayout>
<!-- Space should cover the notch (if it exists) and let other views lay out around it -->
<android.widget.Space
android:id="@+id/cutout_space_view"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center_horizontal|center_vertical"
/>
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/centered_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:clipChildren="false"
android:gravity="center_horizontal|center_vertical"/>
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|end"
>
<LinearLayout
android:id="@+id/system_icons"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
<com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:paddingEnd="@dimen/signal_cluster_battery_padding"
android:gravity="center_vertical"
android:orientation="horizontal"/>
<com.android.systemui.BatteryMeterView android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:clipToPadding="false"
android:clipChildren="false"
android:textAppearance="@style/TextAppearance.StatusBar.Clock" />
<android.widget.Space
android:layout_width="2dp"
android:layout_height="match_parent"
/>
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:paddingBottom="2dip"
android:singleLine="true"
android:gravity="center_vertical|end"
/>
</LinearLayout>
</com.android.keyguard.AlphaOptimizedLinearLayout>
二、状态栏右侧系统图标显示过多时显示一个点
在源码中,控制右侧系统图标显示的有两个限制:
1、图标个数
2、宽度限制
主要方法:onMeasure 和 calculateIconTranslations (下方贴上源代码)
在onMeasure中会测量水滴右边区域能放的下的图标的总宽度(totalWidth)
在calculateIconTranslations中会判断如果图标个数大于限定个数并且剩余宽度不足以绘制一个图标时,就去绘制那个点,达到开始那张图的效果
// Max 8 status icons including battery
private static final int MAX_ICONS = 7;
安卓原生定义了最大显示图标个数是8个(包括电池图标)
mUnderflowWidth :显示的点标框的宽度
mShouldRestrictIcons:是否需要限制图标的一个标志位,原生代码中的下拉后的状态栏是不限制图标的,就是在QuickStatusBarHeaderController.java中控制这个变量为false后就不会去画点
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mMeasureViews.clear();
int mode = MeasureSpec.getMode(widthMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);//当前View的宽度
final int count = getChildCount();
// Collect all of the views which want to be laid out
for (int i = 0; i < count; i++) {
StatusIconDisplayable icon = (StatusIconDisplayable) getChildAt(i);
if (icon.isIconVisible() && !icon.isIconBlocked()
&& !mIgnoredSlots.contains(icon.getSlot())) {
mMeasureViews.add((View) icon);
}
}
int visibleCount = mMeasureViews.size();
int maxVisible = visibleCount <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1;
int totalWidth = mPaddingLeft + mPaddingRight;
boolean trackWidth = true;
// Measure all children so that they report the correct width
int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
mNeedsUnderflow = mShouldRestrictIcons && visibleCount > MAX_ICONS;
for (int i = 0; i < mMeasureViews.size(); i++) {
// Walking backwards
View child = mMeasureViews.get(visibleCount - i - 1);
measureChild(child, childWidthSpec, heightMeasureSpec);
// mShouldRestrictIcons:是否限制图标的一个判断,原生代码中的下拉后的状态栏是
//不限制图标的,就是在QuickStatusBarHeaderController.java中控制这个变量
if (mShouldRestrictIcons) {
if (i < maxVisible && trackWidth) {
//把不超过最大个数的所有图标的宽度加起来
totalWidth += getViewTotalMeasuredWidth(child);
} else if (trackWidth) {
// We've hit the icon limit; add space for dots
//mUnderflowWidth:最后加上圆点的宽度
totalWidth += mUnderflowWidth;
trackWidth = false;
}
} else {
totalWidth += getViewTotalMeasuredWidth(child);
}
}
if (mode == MeasureSpec.EXACTLY) {
//如果图标个数没有超过最大限制的数量但是图标的总宽度大于View的宽度
//此时也是需要显示点的,将mNeedsUnderflow 设置为true
if (!mNeedsUnderflow && totalWidth > width) {
mNeedsUnderflow = true;
}
setMeasuredDimension(width, MeasureSpec.getSize(heightMeasureSpec));
} else {
if (mode == MeasureSpec.AT_MOST && totalWidth > width) {
mNeedsUnderflow = true;
totalWidth = width;
}
setMeasuredDimension(totalWidth, MeasureSpec.getSize(heightMeasureSpec));
}
}
private void calculateIconTranslations() {
mLayoutStates.clear();
float width = getWidth();
//总宽度减去PaddingEnd,获取不显示图标时最大的X偏移量
float translationX = width - getPaddingEnd();
float contentStart = getPaddingStart();
//获取当前需要显示的图标个数
int childCount = getChildCount();
// Underflow === don't show content until that index
if (DEBUG) android.util.Log.d(TAG, "calculateIconTranslations: start=" + translationX
+ " width=" + width + " underflow=" + mNeedsUnderflow);
// Collect all of the states which want to be visible
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
StatusIconDisplayable iconView = (StatusIconDisplayable) child;
StatusIconState childState = getViewStateFromChild(child);
if (!iconView.isIconVisible() || iconView.isIconBlocked()
|| mIgnoredSlots.contains(iconView.getSlot())) {
//满足以上条件设置图标状态为隐藏
childState.visibleState = STATE_HIDDEN;
if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible");
continue;
}
//设置图标状态为显示
childState.visibleState = STATE_ICON;
//总X偏移量减去图标的宽度,己算当前图标的X轴偏移量,
//如果此时计算结果为负数,则说明View中剩余的宽度不足以将此图标显示完全
childState.xTranslation = translationX - getViewTotalWidth(child);
mLayoutStates.add(0, childState);
translationX -= getViewTotalWidth(child);
}
// Show either 1-MAX_ICONS icons, or (MAX_ICONS - 1) icons + overflow
int totalVisible = mLayoutStates.size();
int maxVisible = totalVisible <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1;
mUnderflowStart = 0;
int visible = 0;
int firstUnderflowIndex = -1;
for (int i = totalVisible - 1; i >= 0; i--) {
StatusIconState state = mLayoutStates.get(i);
// Allow room for underflow if we found we need it in onMeasure
//如果需要画点,并且当前图标的X偏移量小于点标框的宽度+padding时,记录当前图标的下标
if (mNeedsUnderflow && (state.xTranslation < (contentStart + mUnderflowWidth))||
(mShouldRestrictIcons && visible >= maxVisible)) {
firstUnderflowIndex = i;
break;
}
mUnderflowStart = (int) Math.max(contentStart, state.xTranslation - mUnderflowWidth);
visible++;
}
if (firstUnderflowIndex != -1) {
int totalDots = 0;
int dotWidth = mStaticDotDiameter + mDotPadding;
int dotOffset = mUnderflowStart + mUnderflowWidth - mIconDotFrameWidth;
for (int i = firstUnderflowIndex; i >= 0; i--) {
//拿到当前下标的图标状态
StatusIconState state = mLayoutStates.get(i);
//系统定义的MAX_DOTS=1,也就是说点图标只会显示一个
if (totalDots < MAX_DOTS) {
state.xTranslation = dotOffset;
//设置当前图标状态为点图标
state.visibleState = STATE_DOT;
dotOffset -= dotWidth;
totalDots++;
} else {
//剩余的图标状态都设置为隐藏
state.visibleState = STATE_HIDDEN;
}
}
}
// Stole this from NotificationIconContainer. Not optimal but keeps the layout logic clean
if(isLayoutRtl()) {
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
StatusIconState state = getViewStateFromChild(child);
state.xTranslation = width - state.xTranslation - child.getWidth();
}
}
}