android 状态栏标,原生安卓状态栏显示网速
终极管理员 知识笔记 105阅读
这是我入职的第一个BUG头疼隔壁实习生一周解决了我多花了几天
其中最大的原因就是我思考复杂了在公司系统上此BUG标题为

请确认Wifi优先级状态栏Wifi被忽略
BUG意思就是当前安卓系统状态栏图标有显示尺寸的测量如果比如需要显示8个图标已经在状态栏绘制不下则显示一个点表示省略而不希望wifi被省略

我思考了一下一直以为就是优先级问题是不是Android底层状态栏有各个图标优先级图标过多的时候优先级高的就不会被隐藏而其实最后参考了公司以前相关BUG的修改其实只是状态栏的图标大小修改即可将图标改小状态栏就可以多一个显示图标Wifi就不会被隐藏。点向后一个图标挪了一位
在尺寸文件中进行修改对这两个尺寸进行改小可以调一下刷机看一下
<dimen namestatus_bar_icon_size>14dip</dimen> <dimen namestatus_bar_system_icon_size>11.5dp</dimen>
值得一提的是上面的逻辑超出的图标隐藏、绘制、测量相关的Java类是StatusIconContainer.Java虽然和BUG不在这改记录一下
/** * StatusIconContainer 是 StatusBarMobileView 内部的一个自定义视图容器用于管理状态图标的显示。 * 它负责在 StatusBarMobileView 中管理状态图标的布局和可见性。 */public class StatusIconContainer extends AlphaOptimizedLinearLayout { //TAG private static final String TAG StatusIconContainer; //是否打开DEBUG模式会Log相关信息 private static final boolean DEBUG false; //是否打开DEBUG_OVERFLOW会在状态栏绘制边框用来显示区域什么的 private static final boolean DEBUG_OVERFLOW false; // 最多可以显示 8 个状态图标包括电池图标 private static final int MAX_ICONS 7; // private static final int MAX_ICONS 8; //点的个数 private static final int MAX_DOTS 1; //Dot 点 Icon图标 相关属性 private int mDotPadding; private int mIconSpacing; private int mStaticDotDiameter; private int mUnderflowWidth; private int mUnderflowStart 0; // 是否可以在溢出空间中绘制 private boolean mNeedsUnderflow; // 单个 StatusBarIconView 的点图标在此宽度内居中显示 private int mIconDotFrameWidth; private boolean mShouldRestrictIcons true; // 用于计算哪些状态要在布局期间可见 private ArrayList<StatusIconState> mLayoutStates new ArrayList<>(); // 为了正确计数和测量 private ArrayList<View> mMeasureViews new ArrayList<>(); // 任何被忽略的图标将不会被添加为子视图 private ArrayList<String> mIgnoredSlots new ArrayList<>(); /** * 创建 StatusIconContainer 的构造函数。 * * param context Android 上下文对象 */ public StatusIconContainer(Context context) { this(context, null); } /** * 创建 StatusIconContainer 的构造函数。 * * param context Android 上下文对象 * param attrs 属性集 */ public StatusIconContainer(Context context, AttributeSet attrs) { super(context, attrs); initDimens(); setWillNotDraw(!DEBUG_OVERFLOW); } Override protected void onFinishInflate() { super.onFinishInflate(); } /** * 设置是否限制状态图标的显示。 * * param should 是否应该限制状态图标的显示 */ public void setShouldRestrictIcons(boolean should) { mShouldRestrictIcons should; } /** * 检查是否正在限制状态图标的显示。 * * return 如果正在限制状态图标的显示则返回 true否则返回 false。 */ public boolean isRestrictingIcons() { return mShouldRestrictIcons; } /** * 初始化尺寸参数。 */ private void initDimens() { // 这是与 StatusBarIconView 使用相同的值 mIconDotFrameWidth getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_icon_size); mDotPadding getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding); mIconSpacing getResources().getDimensionPixelSize(R.dimen.status_bar_system_icon_spacing); // 点图标 int radius getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); // 计算点的直径 R.dimen.overflow_dot_radius 2dp mStaticDotDiameter 2 * radius; // 不能超过宽度 mIconDotFrameWidth 图标宽度 最大图标个数-1可能还有个点所以-1 每个宽高 mUnderflowWidth mIconDotFrameWidth (MAX_DOTS - 1) * (mStaticDotDiameter mDotPadding); android.util.Log.d(TAG, initDimens: mUnderflowStart); } Override protected void onLayout(boolean changed, int l, int t, int r, int b) { float midY getHeight() / 2.0f; // 首先对所有子视图进行布局以便稍后移动它们 for (int i 0; i < getChildCount(); i) { View child getChildAt(i); int width child.getMeasuredWidth(); int height child.getMeasuredHeight(); int top (int) (midY - height / 2.0f); child.layout(0, top, width, top height); } resetViewStates(); calculateIconTranslations(); applyIconStates(); }//绘制 Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (DEBUG_OVERFLOW) { //如果开启了DEBUG_OVERFLOW模式画框框 Paint paint new Paint(); paint.setStyle(Style.STROKE); paint.setColor(Color.RED); // 显示边界框 canvas.drawRect(getPaddingStart(), 0, getWidth() - getPaddingEnd(), getHeight(), paint); // 显示溢出框 paint.setColor(Color.GREEN); canvas.drawRect( mUnderflowStart, 0, mUnderflowStart mUnderflowWidth, getHeight(), paint); } } //测量 Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mMeasureViews.clear(); int mode MeasureSpec.getMode(widthMeasureSpec); final int width MeasureSpec.getSize(widthMeasureSpec); final int count getChildCount(); // 收集所有希望进行布局的视图 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; // 测量所有子视图以便它们报告正确的宽度 int childWidthSpec MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED); mNeedsUnderflow mShouldRestrictIcons && visibleCount > MAX_ICONS; for (int i 0; i < visibleCount; i) { View child mMeasureViews.get(i); measureChild(child, childWidthSpec, heightMeasureSpec); int spacing i visibleCount - 1 ? 0 : mIconSpacing; if (mShouldRestrictIcons) { if (i < maxVisible && trackWidth) { totalWidth getViewTotalMeasuredWidth(child) spacing; } else if (trackWidth) { // 达到图标限制为点图标添加空间 totalWidth mUnderflowWidth; trackWidth false; } } else { totalWidth getViewTotalMeasuredWidth(child) spacing; } } if (mode MeasureSpec.EXACTLY) { 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)); } } Override public void onViewAdded(View child) { super.onViewAdded(child); StatusIconState vs new StatusIconState(); vs.justAdded true; child.setTag(R.id.status_bar_view_state_tag, vs); } Override public void onViewRemoved(View child) { super.onViewRemoved(child); child.setTag(R.id.status_bar_view_state_tag, null); } /** * 添加要忽略的图标槽的名称。它将不会显示在布局中也不会被测量。 * * param slotName 图标的名称就像在 frameworks/base/core/res/res/values/config.xml 中定义的那样 */ public void addIgnoredSlot(String slotName) { android.util.Log.d(TAG, addIgnoredSlot: slotName); boolean added addIgnoredSlotInternal(slotName); if (added) { requestLayout(); } } /** * 添加要忽略的图标槽的名称的列表。 * * param slots 要忽略的图标的名称列表 */ public void addIgnoredSlots(List<String> slots) { for (String slot : slots) { android.util.Log.d(TAG, addIgnoredSlots: slot); } boolean willAddAny false; for (String slot : slots) { willAddAny | addIgnoredSlotInternal(slot); } if (willAddAny) { requestLayout(); } } /** * 内部添加要忽略的图标槽名称。 * * param slotName 图标的名称就像在 frameworks/base/core/res/res/values/config.xml 中定义的那样 * return 如果成功添加则返回 true否则返回 false */ private boolean addIgnoredSlotInternal(String slotName) { android.util.Log.d(TAG, addIgnoredSlotInternal: slotName); if (mIgnoredSlots.contains(slotName)) { return false; } mIgnoredSlots.add(slotName); return true; } /** * 从忽略的图标槽中移除一个名称。 * * param slotName 要移除的图标槽的名称 */ public void removeIgnoredSlot(String slotName) { android.util.Log.d(TAG, removeIgnoredSlot: slotName); boolean removed mIgnoredSlots.remove(slotName); if (removed) { requestLayout(); } } /** * 从忽略的图标槽中移除名称的列表。 * * param slots 要移除的图标槽的名称列表 */ public void removeIgnoredSlots(List<String> slots) { for (String slot: slots) { android.util.Log.d(TAG, removeIgnoredSlot: slot); } boolean removedAny false; for (String slot : slots) { removedAny | mIgnoredSlots.remove(slot); } if (removedAny) { requestLayout(); } } /** * 设置要忽略的图标槽的列表清除当前的列表。 * * param slots 要忽略的图标槽的名称列表 */ public void setIgnoredSlots(List<String> slots) { mIgnoredSlots.clear(); addIgnoredSlots(slots); } /** * 返回与特定槽名称相对应的视图。 * 仅用于操作它如何呈现。 * * param slot 槽名称与 com.android.internal.R.config_statusBarIcons 中定义的名称相对应 * return 如果此容器拥有相应的视图则返回该视图否则返回 null */ public View getViewForSlot(String slot) { for (int i 0; i < getChildCount(); i) { View child getChildAt(i); if (child instanceof StatusIconDisplayable && ((StatusIconDisplayable) child).getSlot().equals(slot)) { return child; } } return null; } /** * 布局从右到左发生。 */ private void calculateIconTranslations() { mLayoutStates.clear(); float width getWidth(); float translationX width - getPaddingEnd(); float contentStart getPaddingStart(); int childCount getChildCount(); // Underflow 不显示内容直到此索引 if (DEBUG) Log.d(TAG, calculateIconTranslations: start translationX width width underflow mNeedsUnderflow); // 收集所有希望可见的状态 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; } // 移动 translationX 到布局的位置以添加视图而不截断子视图 translationX - getViewTotalWidth(child); childState.visibleState STATE_ICON; childState.xTranslation translationX; mLayoutStates.add(0, childState); // 为下一个视图移动 translationX 以腾出间隔 translationX - mIconSpacing; } // 显示 1 至 MAX_ICONS 个图标或 (MAX_ICONS - 1) 个图标 溢出 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); // 如果在测量时发现需要溢出则允许在此之前腾出空间 if (mNeedsUnderflow && (state.xTranslation < (contentStart mUnderflowWidth)) || (mShouldRestrictIcons && visible > maxVisible)) { firstUnderflowIndex i; break; } mUnderflowStart (int) Math.max( contentStart, state.xTranslation - mUnderflowWidth - mIconSpacing); 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); if (totalDots < MAX_DOTS) { state.xTranslation dotOffset; state.visibleState STATE_DOT; dotOffset - dotWidth; totalDots; } else { state.visibleState STATE_HIDDEN; } } } // 从 NotificationIconContainer 中拿来的不是最优解但保持布局逻辑简洁 if (isLayoutRtl()) { for (int i 0; i < childCount; i) { View child getChildAt(i); StatusIconState state getViewStateFromChild(child); state.xTranslation width - state.xTranslation - child.getWidth(); } } } private void applyIconStates() { for (int i 0; i < getChildCount(); i) { View child getChildAt(i); StatusIconState vs getViewStateFromChild(child); if (vs ! null) { vs.applyToView(child); } } } private void resetViewStates() { for (int i 0; i < getChildCount(); i) { View child getChildAt(i); StatusIconState vs getViewStateFromChild(child); if (vs null) { continue; } vs.initFrom(child); vs.alpha 1.0f; vs.hidden false; } } private static Nullable StatusIconState getViewStateFromChild(View child) { return (StatusIconState) child.getTag(R.id.status_bar_view_state_tag); } private static int getViewTotalMeasuredWidth(View child) { return child.getMeasuredWidth() child.getPaddingStart() child.getPaddingEnd(); } private static int getViewTotalWidth(View child) { return child.getWidth() child.getPaddingStart() child.getPaddingEnd(); } public static class StatusIconState extends ViewState { /// StatusBarIconView.STATE_* public int visibleState STATE_ICON; public boolean justAdded true; // 从视图末尾的距离是最相关的用于动画 float distanceToViewEnd -1; Override public void applyToView(View view) { float parentWidth 0; if (view.getParent() instanceof View) { parentWidth ((View) view.getParent()).getWidth(); } float currentDistanceToEnd parentWidth - xTranslation; if (!(view instanceof StatusIconDisplayable)) { return; } StatusIconDisplayable icon (StatusIconDisplayable) view; AnimationProperties animationProperties null; boolean animateVisibility true; // 用于计算哪些状态在布局过程中是可见的找出哪些属性的状态转换(如果有的话)我们需要动画 // 确定要动画的状态转换的属性如果有 if (justAdded || icon.getVisibleState() STATE_HIDDEN && visibleState STATE_ICON) { // 图标正在出现通过将其放在它将出现的位置并动画 alpha 来淡入它 super.applyToView(view); view.setAlpha(0.f); icon.setVisibleState(STATE_HIDDEN); animationProperties ADD_ICON_PROPERTIES; } else if (icon.getVisibleState() ! visibleState) { if (icon.getVisibleState() STATE_ICON && visibleState STATE_HIDDEN) { // 消失不要执行任何复杂的操作 animateVisibility false; } else { // 所有其他转换到/从点等 animationProperties ANIMATE_ALL_PROPERTIES; } } else if (visibleState ! STATE_HIDDEN && distanceToViewEnd ! currentDistanceToEnd) { // 可见性不在发生变化只需动画位置 animationProperties X_ANIMATION_PROPERTIES; } icon.setVisibleState(visibleState, animateVisibility); if (animationProperties ! null) { view.animate().cancel(); icon.addTransformationToViewGroup(view, animationProperties, null); } icon.applyInShelfTransformation(view, this, animationProperties, animationProperties); } }}
标签: