Android 12.0 电池图标客制化,横放,仿苹果。

目录

一、概述

二、电池图标显示所在源码分析

三、自定义电池图标 

四、总结


 

一、概述

原生的电池图标是竖向,并且很小,不太美观。现客户需求状态栏的电池图标变为横放的。众所周知,状态栏的修改在SystemUI里。

效果图:

二、电池图标显示所在源码分析

 状态栏布局文件是status_bar。路径:SystemUI\res\layout\status_bar.xml

<?xml version="1.0" encoding="utf-8"?>


<!--    android:background="@drawable/status_bar_closed_default_background" -->
<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:layout_width="match_parent"
    android:layout_height="@dimen/status_bar_height"
    android:id="@+id/status_bar"
    android:orientation="vertical"
    android:focusable="false"
    android:descendantFocusability="afterDescendants"
    android:accessibilityPaneTitle="@string/status_bar"
    >

    <ImageView
        android:id="@+id/notification_lights_out"
        android:layout_width="@dimen/status_bar_icon_size"
        android:layout_height="match_parent"
        android:paddingStart="@dimen/status_bar_padding_start"
        android:paddingBottom="2dip"
        android:src="@drawable/ic_sysbar_lights_out_dot_small"
        android:scaleType="center"
        android:visibility="gone"
        />

    <LinearLayout android:id="@+id/status_bar_contents"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingStart="@dimen/status_bar_padding_start"
        android:paddingEnd="@dimen/status_bar_padding_end"
        android:paddingTop="@dimen/status_bar_padding_top"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        >

..省略部分代码..

        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2"
            android:orientation="horizontal"
            android:gravity="center_vertical|end"
            >

            <include layout="@layout/system_icons" />
        </com.android.keyguard.AlphaOptimizedLinearLayout>
    </LinearLayout>

    <ViewStub
        android:id="@+id/emergency_cryptkeeper_text"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout="@layout/emergency_cryptkeeper_text"
    />

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

电池图标就在布局system_icons中,路径:SystemUI\res\layout\system_icons.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:systemui="http://schemas.android.com/apk/res-auto"
    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"
        systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" />

</LinearLayout>

其中BatteryMeterView是自定义View,继承于LinearLayout,实现的接口有BatteryStateChangeCallback,Tunable,DarkReceiver,ConfigurationListener。主要是监听电池状态变化,白天黑夜模式,以及主题等变化。

public class BatteryMeterView extends LinearLayout implements
        BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {}

而锁屏界面的状态栏也是引用到这个BatteryMeterView,在路径SystemUI\src\com\android\systemui\statusbar\phone\KeyguardStatusBarView.java中

private BatteryMeterView mBatteryView;

OK,明白了大概的逻辑,就可以自己试着重构了。

三、自定义电池图标 

如果在原文件 BatteryMeterView中修改太过麻烦,所以我们可以模仿BatteryMeterView,直接自己写一个自定义View。

直接上主菜!

1.在system_icons.xml中替换布局。 

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:systemui="http://schemas.android.com/apk/res-auto"
    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"/>

    <!--add by huangjiawei,replace bird battery meter view
    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:clipToPadding="false"
        android:clipChildren="false"
        systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" />
    -->
    <include layout="@layout/bird_status_bar_battery"/>
    <!-- @} -->
</LinearLayout>

 bird_status_bar_battery.xml

<?xml version="1.0" encoding="utf-8"?>
<com.bird.systemui.BirdBatteryMeterView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/battery"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:orientation="horizontal">

    <TextView
        android:id="@+id/battery_outside_percent"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:paddingRight="2dp"
        android:textColor="@android:color/white"
        android:textSize="@dimen/bird_battery_outside_text_size"
        android:visibility="gone" />

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center">

        <ImageView
            android:id="@+id/battery_border"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@drawable/bird_ic_statusbar_battery" />

        <ImageView
            android:id="@+id/battery_level"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@drawable/bird_stat_sys_battery_level_list" />

        <ImageView
            android:id="@+id/battery_inside_charge_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:scaleType="centerInside"
            android:src="@drawable/bird_ic_statusbar_battery_charge_icon"
            android:visibility="gone" />

        <TextView
            android:id="@+id/battery_inside_percent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:paddingEnd="2dp"
            android:textColor="@android:color/white"
            android:textSize="@dimen/bird_battery_inside_text_size"
            android:visibility="gone" />

    </FrameLayout>

    <ImageView
        android:id="@+id/battery_outside_charge_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:paddingStart="2dp"
        android:src="@drawable/bird_ic_statusbar_battery_charge_icon"
        android:visibility="gone" />
</com.bird.systemui.BirdBatteryMeterView>

可以看到,上面的布局是一个横向的LinearLayout。

1.其中有outside和inside的电量百分比,百分比的显示或隐藏可以在设置-电池中去控制,也可以直接写死一种方式。

2.outside的充电图标和inside的充电图标,就是开始充电后显示的闪电图标,可以选择在电池内也可以选择在外侧。充电图标和电量百分比是对立的,如果充电图标inside,则电量百分比outside,反之亦然。

3.border是电池的边界,就是最外的一圈。

4.charge_level用list来管理,一共有10来个等级。电量的多少就是从这里体现。

 

2.布局中的src都是矢量图,具体的矢量图代码在另一篇文章中。可以参考一下。

传送门:http://t.csdn.cn/BgwqF

 3.自定义view,BirdBatteryMeterView同样继承于LinearLayout

package com.bird.systemui;

import android.app.ActivityManager;
import android.content.Context;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Handler;
import android.os.PowerManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.tuner.TunerService.Tunable;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.Optional;

import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
import static com.android.systemui.DejankUtils.whitelistIpcs;

//[状态栏更换新的电池图标][huangjiawei][20230427]

public class BirdBatteryMeterView extends LinearLayout implements
        BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {

    private ImageView mBorderView;
    private ImageView mInsideChargeIconView;
    private ImageView mOutsideChargeIconView;
    private ImageView mLevelView;
    private TextView mInsidePercentView;
    private TextView mOutsidePercentView;

    private BatteryController mBatteryController;
    private int mUser;
    private final SettingObserver mSettingObserver;
    private final boolean mShowPercentAvailable;
    private final CurrentUserTracker mUserTracker;
    private int mNonAdaptedColor;
    private final String mSlotBattery;

    private int mLevel;
    private boolean mShowPercent;
    private boolean mShowPercentIn;
    private boolean mIsPowerSave;
    private boolean mCharging;

    public BirdBatteryMeterView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BirdBatteryMeterView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        BroadcastDispatcher broadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
        mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
        mShowPercentAvailable = context.getResources().getBoolean(
                com.android.internal.R.bool.config_battery_percentage_setting_available);
        mSlotBattery = context.getString(
                com.android.internal.R.string.status_bar_battery);

        mUserTracker = new CurrentUserTracker(broadcastDispatcher) {
            @Override
            public void onUserSwitched(int newUserId) {
                mUser = newUserId;
                getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
                getContext().getContentResolver().registerContentObserver(
                        Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
                        newUserId);
                updateShowPercent();
                updatePercentLevel();
            }
        };
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mBorderView = findViewById(R.id.battery_border);
        mLevelView = findViewById(R.id.battery_level);
        mInsidePercentView = findViewById(R.id.battery_inside_percent);
        mOutsidePercentView = findViewById(R.id.battery_outside_percent);
        mInsideChargeIconView = findViewById(R.id.battery_inside_charge_icon);
        mOutsideChargeIconView = findViewById(R.id.battery_outside_charge_icon);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mBatteryController = Dependency.get(BatteryController.class);
        mBatteryController.addCallback(this);
        mUser = ActivityManager.getCurrentUser();
        getContext().getContentResolver().registerContentObserver(
                Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
        getContext().getContentResolver().registerContentObserver(
                Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
                false, mSettingObserver);

        mUserTracker.startTracking();
        updateShowPercent();
        updatePercentLevel();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mUserTracker.stopTracking();
        mBatteryController.removeCallback(this);
        getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
    }

    @Override
    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
        mNonAdaptedColor = DarkIconDispatcher.getTint(area, this, tint);
        mOutsidePercentView.setTextColor(mNonAdaptedColor);
        mInsidePercentView.setTextColor(mNonAdaptedColor);
        mInsideChargeIconView.setColorFilter(mNonAdaptedColor);
        mOutsideChargeIconView.setColorFilter(mNonAdaptedColor);
        if (!mIsPowerSave) {
            mBorderView.setColorFilter(mNonAdaptedColor);
        }
        if (!isLowLevel() || mCharging) {
            mLevelView.setColorFilter(mNonAdaptedColor);
        }
    }

    @Override
    public void onTuningChanged(String key, String newValue) {
        if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
            ArraySet<String> icons = StatusBarIconController.getIconHideList(
                    getContext(), newValue);
            setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
        }
    }

    @Override
    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
        mLevel = level;
        mCharging = charging;
        updatePercentLevel();
        updatePercentText();
    }

    @Override
    public void onPowerSaveChanged(boolean isPowerSave) {
        PowerManager powerManager = getContext().getSystemService(PowerManager.class);
        mIsPowerSave = powerManager.isPowerSaveMode();
        if (mIsPowerSave) {
            mBorderView.setColorFilter(mContext.getColor(R.color.bird_battry_power_save_color));
        } else {
            mBorderView.setColorFilter(mNonAdaptedColor);
        }
    }

    private final class SettingObserver extends ContentObserver {

        public SettingObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange, @Nullable Uri uri) {
            super.onChange(selfChange, uri);
            updateShowPercent();
            updatePercentLevel();
            if (TextUtils.equals(uri.getLastPathSegment(),
                    Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME)) {
                updatePercentText();
            }
        }
    }

    private void updatePercentText() {
        if (mBatteryController == null) {
            return;
        }

        if (mOutsidePercentView != null || mInsidePercentView != null) {
            if (!mCharging) {
                mBatteryController.getEstimatedTimeRemainingString((String estimate) -> {
                    if (estimate != null) {
                        Optional.ofNullable(mOutsidePercentView).ifPresent(view -> view.setText(estimate));
                        Optional.ofNullable(mInsidePercentView).ifPresent(view -> view.setText(estimate));
                        setContentDescription(getContext().getString(
                                R.string.accessibility_battery_level_with_estimate,
                                mLevel, estimate));
                    } else {
                        setPercentTextAtCurrentLevel();
                    }
                });
            } else {
                setPercentTextAtCurrentLevel();
            }
        } else {
            setContentDescription(
                    getContext().getString(mCharging ? R.string.accessibility_battery_level_charging
                            : R.string.accessibility_battery_level, mLevel));
        }
    }

    private void setPercentTextAtCurrentLevel() {
        mOutsidePercentView.setText(
                NumberFormat.getPercentInstance().format(mLevel / 100f));
        mInsidePercentView.setText(NumberFormat.getNumberInstance().format(mLevel));
        setContentDescription(
                getContext().getString(mCharging ? R.string.accessibility_battery_level_charging
                        : R.string.accessibility_battery_level, mLevel));
    }

    private void updateShowPercent() {
        //读设置中是否显示电量百分比的值
        mShowPercent = 0 != whitelistIpcs(() -> Settings.System
                .getIntForUser(getContext().getContentResolver(),
                        SHOW_BATTERY_PERCENT, 0, mUser));
        //这个值同样可以在设置-电池中加两项去让用户决定用哪种方式,也可以直接像这里的值一样,直接写死一种方式。
        mShowPercentIn = true;

        if (mShowPercentAvailable && mShowPercent) {
            mOutsidePercentView.setVisibility(!mShowPercentIn ? View.VISIBLE : View.GONE);
            mInsidePercentView.setVisibility(mShowPercentIn ? View.VISIBLE : View.GONE);
            updatePercentText();
        } else {
            mOutsidePercentView.setVisibility(View.GONE);
            mInsidePercentView.setVisibility(View.GONE);
        }
    }

    private void updatePercentLevel() {
        //电量百分比和充电图标出现的位置是对立的
        boolean showInsidePercent = mShowPercent && mShowPercentIn;

        mInsideChargeIconView.setVisibility(mCharging && !showInsidePercent
                ? View.VISIBLE : View.GONE);
        mOutsideChargeIconView.setVisibility(mCharging && showInsidePercent
                ? View.VISIBLE : View.GONE);
        mLevelView.setImageLevel(mLevel);
        //设置透明度可以调整充电body的颜色
        if (mCharging) {
            mLevelView.setColorFilter(mNonAdaptedColor);
            mLevelView.setAlpha(0.6f);
        } else {
            if (isLowLevel()) {
                mLevelView.clearColorFilter();
                mLevelView.setAlpha(1.0f);
            } else {
                mLevelView.setColorFilter(mNonAdaptedColor);
                if (showInsidePercent) {
                    mLevelView.setAlpha(0.3f);
                } else {
                    mLevelView.setAlpha(1.0f);
                }
            }
        }
    }

    private boolean isLowLevel() {
        return mLevel <= 20;
    }

    // copy from BatteryMeterView, caller exists
    public void setForceShowPercent(boolean show) {
    }

    // copy from BatteryMeterView, caller exists
    public void setColorsFromContext(Context context) {
    }

    // copy from BatteryMeterView, caller exists
    public void updatePercentView() {
    }

    // copy from BatteryMeterView, caller exists
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    }
}

 

4.在锁屏的状态栏也更换一下自定义View就完事儿了,SystemUI\src\com\android\systemui\statusbar\phone\KeyguardStatusBarView.java

/**
 * The header group on Keyguard.
 */
public class KeyguardStatusBarView extends RelativeLayout implements
        BatteryStateChangeCallback,
        OnUserInfoChangedListener,
        ConfigurationListener,
        SystemStatusAnimationCallback {

........
    private ImageView mMultiUserAvatar;
	//[状态栏更换新的电池图标][huangjiawei][20230427]begin
    //private BatteryMeterView mBatteryView;
    private BirdBatteryMeterView mBatteryView;
	//[状态栏更换新的电池图标][huangjiawei][20230427]end
    private StatusIconContainer mStatusIconContainer;

    private BatteryController mBatteryController;

........

}

四、总结

至此,更换android系统状态栏的电池图标就完成了。自己可以根据需要修改里面的参数,例如宽高,颜色,透明度等等,也可以在设置里添加控制电量百分比和充电图标的选项,仅作参考。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 《Android 12.0 Launcher 指导手册》是针对 Android 12.0 系统中的 Launcher 进行的一份指导手册。Launcher 是 Android 系统中一个非常重要的应用程序,它可以用于显示主屏幕、管理应用程序、控主屏幕布局等功能。在 Android 12.0 系统中,通过针对 Launcher 进行,可以提高用户体验和系统性能。 该手册主要包括 Android 12.0 系统中 Launcher 的设计原则、UI 界面设计、功能模块介绍、应用场景及其具体实现方法等内容。其中,设计原则主要体现在如何让 Launcher 界面更加简洁、美观、易用;UI 界面设计包括主屏幕、文件夹、小部件等的设计方法和规范;功能模块介绍包括搜索、应用程序管理、主题样式等模块的设计原则和实现方法;应用场景包括品牌定、个性UI、默认应用设置、第三方应用集成等方面的应用场景和解决方案。 总体来说,该手册提供了 Android 12.0 系统中 Launcher 进行的全面指导,可以帮助开发者更好地理解、设计和实现 Launcher 功能。这有助于加快系统开发周期、提高用户体验和产品质量,从而促进 Android 生态系统的发展。 ### 回答2: 《Android 12.0 Launcher 指导手册》是一份针对Android 12.0操作系统的指导手册,主要面向Launcher开发者和定厂商,为其提供定Android 12.0 Launcher的技术指导和实践经验。该手册涵盖了Launcher的主题、桌面布局、快捷手势、搜索、文件夹、小部件、通知等各个方面的技术,并提供了详尽的代码示例和技术规范说明。 首先,这份手册详细介绍了如何定Launcher的主题样式和图标,以及在Android 12.0系统中的适配方案。其次,手册列举了桌面布局的几种类型和实现方式,并提供了代码示例和技术指导,帮助开发者和定厂商快速实现各种桌面布局的样式。 同时,手册对Launcher操作过程中的常用手势操作和快捷键进行详细讲解,并且提供了实现这些手势和快捷键的代码示例。此外,手册还对搜索、文件夹、小部件、通知等功能的定进行了详细的说明和实现方案,帮助开发者和定厂商快速实现自己的Launcher。 总之,《Android 12.0 Launcher指导手册》是一份全面、实用的技术指导手册,内容详实,对于Launcher的开发者和定厂商有非常大的参考价值,可以帮助开发者快速实现定的Launcher,提高用户体验和市场竞争力。 ### 回答3: Android 12.0 Launcher 指导手册.pdf 是一本介绍如何自定义 Android 12.0 系统 Launcher 的手册。其中包含了一系列的操作指南,帮助开发者们根据自己的需要来对Launcher进行定。 该手册主要涵盖了以下内容: 1. 界面设计:介绍了如何对主屏幕、文件夹、应用抽屉等界面元素进行样式定,并提供了一些设计建议。 2. 功能扩展:介绍了如何添加新的功能、快捷方式,以及如何对已有功能进行修改和调整。 3. 稳定性优:提供了一些优 Launcher 稳定性的技巧和建议,如如何减少应用闪退、ANR等问题。 4. 性能调优:介绍了如何减少Launcher的耗电量、内存消耗和刷新次数,提高用户体验。 5. 其他建议:介绍了一些开发者们需要注意的其他事项,如如何遵守Google的各项规定和诸如此类的问题。 总之,Android 12.0 Launcher 指导手册.pdf涵盖了从界面设计到性能优的多个方面,为开发者们自定义Launcher提供了全面的指导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值