欢迎来到飞鸟慕鱼博客,开始您的技术之旅!
当前位置: 首页知识笔记正文

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);        }    }}

标签:
声明:无特别说明,转载请标明本文来源!