Android Q : 安卓源码、水滴屏适配状态栏图标(图标过多时显示一个点)

源码路径:com.android.systemui.statusbar.phone/StatusIconContainer.java
布局文件路径:SystemUI/res/layout/status_bar.xml

一、状态栏布局

在没下拉状态栏时,原生android会将状态栏分为两部分(如下图及布局源码)
1、android:id="@+id/notification_icon_area
2、android:id="@+id/system_icon_area"

从下面的布局文件可以看到这两块区域的权重都是1,左边放通知图标,右边放系统图标,中间留有一块控件用来放缺口(也就是水滴或刘海的位置),在这里如果没有水滴的话可以修改权重,来放置更多的系统图标或通知图标(有水滴的话改了图标过多时就会遮挡住图标)
Android Q : 安卓源码、水滴屏适配状态栏图标(图标过多时显示一个点)_第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" />

                <!-- HCT lishuo for show new battery icon style -->
                <include layout="@layout/hct_status_bar_battery"/>
                <!-- HCT lishuo for show new battery icon style end -->
                <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个(包括电池图标)

@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();
        // 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);
            if (mShouldRestrictIcons) {
                if (i < maxVisible && trackWidth) {
                    totalWidth += getViewTotalMeasuredWidth(child);
                } else if (trackWidth) {
                    // We've hit the icon limit; add space for dots
                    totalWidth += mUnderflowWidth;
                    trackWidth = false;
                }
            } else {
                totalWidth += getViewTotalMeasuredWidth(child);
            }
        }

        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));
        }
    }
private void calculateIconTranslations() {
        mLayoutStates.clear();
        float width = getWidth();
        float translationX = width - getPaddingEnd();
        float contentStart = getPaddingStart();
        //HCT: lishuo statusbar new style
        if(isLayoutOpposite){
            translationX = width - getPaddingStart();
            contentStart = getPaddingEnd();
        }
        //HCT: lishuo statusbar new style end
        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

        //HCT: lishuo statusbar new style, Exchange SIM icons position
        View firstSimIconView = null;
        View secondSimIconView= null;
        if(isLayoutOpposite){
            for (int i = childCount - 1; i >= 0; i--) {
                View child = getChildAt(i);
                if(child instanceof StatusBarMobileView && firstSimIconView == null){
                    firstSimIconView = child;
                }else if(child instanceof StatusBarMobileView && firstSimIconView != null){
                    secondSimIconView = child;
                    break;
                }
            }
        }
        //HCT: lishuo statusbar new style, Exchange SIM icons position end
        for (int i = childCount - 1; i >= 0; i--) {
            View child = getChildAt(i);
            //HCT: lishuo statusbar new style
            if(isLayoutOpposite && firstSimIconView != null && secondSimIconView != null){
                if(child.equals(firstSimIconView)){
                    child = secondSimIconView;
                }else if(child.equals(secondSimIconView)){
                    child = firstSimIconView;
                }
            }
            //HCT: lishuo statusbar new style end
            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;
            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
            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);
                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(isLayoutOpposite){
            if (isLayoutRtl()) {
            }else{
                for (int i = 0; i < childCount; i++) {
                    View child = getChildAt(i);
                    StatusIconState state = getViewStateFromChild(child);
                    state.xTranslation = width - state.xTranslation - child.getWidth();
                }
            }
        }else if (isLayoutRtl()) {
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                StatusIconState state = getViewStateFromChild(child);
                state.xTranslation = width - state.xTranslation - child.getWidth();
            }
        }
    }

你可能感兴趣的:(android)