0
点赞
收藏
分享

微信扫一扫

Android系统开发之黑白主题动态切换

艾晓雪 2024-02-04 阅读 18

前言

在android 10.0,新增一个深色主题功能,具体如官网说明:
https://developer.android.google.cn/guide/topics/ui/look-and-feel/darktheme

Android系统开发之黑白主题动态切换_黑白主题


说白了,就是一个黑白主题动态切换,那对于android 10.0以前的版本,就不支持这个功能了。


需求来源

一天,领导来找我,说小明,现在有一个android的深色主题功能开发,就是黑白主题,你在我们的项目上也来搞一个吧。
我一查项目android版本为8.1,这....,不支持深色主题功能。但是领导要求要,好吧,来开整。


方案一:设置应用黑白不同的主题方案

1.设置应用的黑白主题配置

主要是将应用的Application主题设置动态根据不同的主题来设置

public class MtkSettingsApplication extends Application {
......
    public void onCreate() {
        super.onCreate();
        ......
        int themeMode = Helper.getThemeMode(context);
        if(themeMode == Settings.Secure.THEME_MODE_LIGHT){
            //白色主题
            setTheme(R.style.Theme_Settings);
        }else if(themeMode == Settings.Secure.THEME_MODE_DARK){
            //黑色主题
            setTheme(R.style.Theme_Settings_Dark);
        }
    }
......
}

MtkSettings\res\values\themes.xml

<style name="Theme.Settings" parent="Theme.SettingsBase">
    <item name="preferenceTheme">@style/PreferenceTheme</item>
    <item name="android:listPreferredItemHeight">72dip</item>
    <item name="*android:preferenceHeaderPanelStyle">@style/PreferenceHeaderPanelSinglePane</item>
    <item name="*android:preferencePanelStyle">@style/PreferencePanelSinglePane</item>
    <item name="*android:preferenceListStyle">@style/PreferenceHeaderListSinglePane</item>
    <item name="*android:preferenceFragmentListStyle">@style/PreferenceFragmentListSinglePane</item>
    <item name="*android:preferenceFragmentPaddingSide">@dimen/settings_side_margin</item>

    <item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
    <item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_holo_dark</item>
    <item name="wifi_signal">@drawable/wifi_signal</item>
    <item name="wifi_signal_color">?android:attr/colorAccent</item>
    <item name="wifi_friction">@drawable/wifi_friction</item>
    <item name="side_margin">@dimen/settings_side_margin</item>
    <item name="suwListItemIconColor">?android:attr/colorAccent</item>

    <!-- Redefine the ActionBar style for contentInsetStart -->
    <item name="android:actionBarStyle">@style/Theme.ActionBar</item>

    <item name="switchBarTheme">@style/ThemeOverlay.SwitchBar.Settings</item>

    <item name="preferenceBackgroundColor">@drawable/preference_background</item>

    <!-- For all Alert Dialogs -->
    <item name="android:alertDialogTheme">@style/Theme.AlertDialog</item>

    <item name="*android:lockPatternStyle">@style/LockPatternStyle.Setup</item>

    <!-- For battery status icons in -->
    <item name="batteryGoodColor">@color/battery_good_color_light</item>
    <item name="batteryMaybeColor">@color/battery_maybe_color_light</item>
    <item name="batteryBadColor">@color/battery_bad_color_light</item>
</style>


<style name="Theme.Settings_Dark" parent="Theme.SettingsBase">
    <item name="preferenceTheme">@style/PreferenceTheme</item>
    <item name="android:listPreferredItemHeight">72dip</item>
    <item name="*android:preferenceHeaderPanelStyle">@style/PreferenceHeaderPanelSinglePane</item>
    <item name="*android:preferencePanelStyle">@style/PreferencePanelSinglePane</item>
    <item name="*android:preferenceListStyle">@style/PreferenceHeaderListSinglePane</item>
    <item name="*android:preferenceFragmentListStyle">@style/PreferenceFragmentListSinglePane</item>
    <item name="*android:preferenceFragmentPaddingSide">@dimen/settings_side_margin</item>

    <item name="fingerprint_layout_theme">@style/FingerprintLayoutTheme</item>
    <item name="ic_menu_moreoverflow">@*android:drawable/ic_menu_moreoverflow_holo_dark</item>
    <item name="wifi_signal">@drawable/wifi_signal</item>
    <item name="wifi_signal_color">?android:attr/colorAccent</item>
    <item name="wifi_friction">@drawable/wifi_friction</item>
    <item name="side_margin">@dimen/settings_side_margin</item>
    <item name="suwListItemIconColor">?android:attr/colorAccent</item>

    <!-- Redefine the ActionBar style for contentInsetStart -->
    <item name="android:actionBarStyle">@style/Theme.ActionBar_Dark</item>

    <item name="switchBarTheme">@style/ThemeOverlay.SwitchBar.Settings_Dark</item>

    <item name="preferenceBackgroundColor">@*android:color/black</item>

    <!-- For all Alert Dialogs -->
    <item name="android:alertDialogTheme">@style/Theme.AlertDialog_Dark</item>
    <item name="*android:lockPatternStyle">@style/LockPatternStyle.Setup</item>

    <!-- For battery status icons in -->
    <item name="batteryGoodColor">@color/battery_good_color_light</item>
    <item name="batteryMaybeColor">@color/battery_maybe_color_light</item>
    <item name="batteryBadColor">@color/battery_bad_color_light</item>

    <item name="*android:isLightTheme">false</item>
    <item name="android:windowBackground">@*android:color/black</item>
    <item name="android:colorPrimary">@*android:color/primary_device_default_settings</item>
    <item name="android:colorPrimaryDark">@*android:color/primary_dark_device_default_settings</item>
    <item name="android:colorSecondary">@*android:color/secondary_device_default_settings</item>
    <item name="android:colorAccent">@*android:color/accent_device_default_dark</item>
    <item name="android:colorControlNormal">?android:attr/textColorPrimary</item>
    <item name="android:colorBackgroundFloating">@*android:color/material_grey_900</item>
    <item name="android:panelColorBackground">@*android:color/material_grey_800</item>

    <item name="android:background">@*android:color/material_grey_800</item>

    <item name="android:colorPrimary">@*android:color/white</item>
    <item name="android:colorPrimaryDark">@*android:color/white</item>
    <item name="android:colorSecondary">@*android:color/white</item>


    <item name="android:colorForeground">@*android:color/foreground_material_dark</item>
    <item name="android:colorForegroundInverse">@*android:color/foreground_material_light</item>
    <item name="android:colorBackground">@*android:color/material_grey_900</item>
    <item name="android:colorBackgroundFloating">@*android:color/background_floating_material_dark</item>
    <item name="android:colorBackgroundCacheHint">@*android:color/background_cache_hint_selector_material_dark</item>
    <item name="android:colorError">@*android:color/error_color_material_dark</item>
    <item name="android:textColorPrimary">@*android:color/text_color_primary</item>
    <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_light</item>
    <item name="android:textColorPrimaryDisableOnly">@*android:color/primary_text_disable_only_material_dark</item>
    <item name="android:textColorSecondary">@*android:color/text_color_secondary</item>
    <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_light</item>
    <item name="android:textColorTertiary">@*android:color/secondary_text_material_dark</item>
    <item name="android:textColorTertiaryInverse">@*android:color/secondary_text_material_light</item>
    <item name="android:textColorHint">@*android:color/hint_foreground_material_dark</item>
    <item name="android:textColorHintInverse">@*android:color/hint_foreground_material_light</item>
    <item name="android:textColorHighlight">@*android:color/highlighted_text_material</item>
    <item name="android:textColorHighlightInverse">@*android:color/highlighted_text_material</item>

    <item name="android:textColorAlertDialogListItem">@*android:color/white</item>
    <item name="android:textCheckMark">@*android:drawable/indicator_check_mark_dark</item>
    <item name="android:textCheckMarkInverse">@*android:drawable/indicator_check_mark_light</item>
    <item name="android:colorControlHighlight">@*android:color/ripple_material_dark</item>
    <item name="android:colorButtonNormal">@*android:color/btn_default_material_dark</item>

    <item name="android:textColor">@*android:color/white</item>

    <item name="android:selectableItemBackground">@*android:color/material_grey_800</item>
</style>

2.设置应用对应Activity的黑白主题配置

主要是将应用对应Activity的动态根据不同的黑白主题来设置,如SettingsActivity:

public class SettingsActivity extends SettingsDrawerActivity
        implements PreferenceManager.OnPreferenceTreeClickListener,
        PreferenceFragment.OnPreferenceStartFragmentCallback,
        ButtonBarHandler, FragmentManager.OnBackStackChangedListener {
.....
    protected void onCreate(Bundle savedState) {
        super.onCreate(savedState);
        .....
        int themeMode = Helper.getThemeMode(getApplicationContext());
        if(themeMode == android.provider.Settings.Secure.THEME_MODE_LIGHT){
            //白色主题
            setTheme(R.style.Theme_SubSettings);
        }else if(themeMode == android.provider.Settings.Secure.THEME_MODE_DARK){
            //黑色主题
            setTheme(R.style.Theme_SubSettings_Dark);
        }
        .....
    }
.....
}

MtkSettings\res\values\themes.xml

<style name="Theme.SubSettings" parent="Theme.Settings">
    <!-- Redefine the ActionBar style for contentInsetStart -->
    <item name="android:actionBarStyle">@style/Theme.ActionBar.SubSettings</item>
    <item name="switchBarTheme">@style/ThemeOverlay.SwitchBar.Settings</item>
</style>

<style name="Theme.SubSettings_Dark" parent="Theme.Settings">
    <item name="android:actionBarStyle">@style/Theme.ActionBar.SubSettings_Dark</item>
    <item name="switchBarTheme">@style/ThemeOverlay.SwitchBar.Settings_Dark</item>

    <item name="*android:isLightTheme">false</item>
    <item name="android:windowBackground">@*android:color/black</item>
    <item name="android:colorPrimary">@*android:color/primary_device_default_settings</item>
    <item name="android:colorPrimaryDark">@*android:color/primary_dark_device_default_settings</item>
    <item name="android:colorSecondary">@*android:color/secondary_device_default_settings</item>
    <item name="android:colorAccent">@*android:color/accent_device_default_dark</item>
    <item name="android:colorControlNormal">?android:attr/textColorPrimary</item>
    <item name="android:colorBackgroundFloating">@*android:color/material_grey_900</item>
    <item name="android:panelColorBackground">@*android:color/material_grey_800</item>

    <item name="android:background">@*android:color/material_grey_800</item>

    <item name="android:colorPrimary">@*android:color/white</item>
    <item name="android:colorPrimaryDark">@*android:color/white</item>
    <item name="android:colorSecondary">@*android:color/white</item>

    <item name="android:colorForeground">@*android:color/foreground_material_dark</item>
    <item name="android:colorForegroundInverse">@*android:color/foreground_material_light</item>
    <item name="android:colorBackground">@*android:color/material_grey_900</item>
    <item name="android:colorBackgroundFloating">@*android:color/background_floating_material_dark</item>
    <item name="android:colorBackgroundCacheHint">@*android:color/background_cache_hint_selector_material_dark</item>
    <item name="android:colorError">@*android:color/error_color_material_dark</item>
    <item name="android:textColorPrimary">@*android:color/text_color_primary</item>
    <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_light</item>
    <item name="android:textColorPrimaryDisableOnly">@*android:color/primary_text_disable_only_material_dark</item>
    <item name="android:textColorSecondary">@*android:color/text_color_secondary</item>
    <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_light</item>
    <item name="android:textColorTertiary">@*android:color/secondary_text_material_dark</item>
    <item name="android:textColorTertiaryInverse">@*android:color/secondary_text_material_light</item>
    <item name="android:textColorHint">@*android:color/hint_foreground_material_dark</item>
    <item name="android:textColorHintInverse">@*android:color/hint_foreground_material_light</item>
    <item name="android:textColorHighlight">@*android:color/highlighted_text_material</item>
    <item name="android:textColorHighlightInverse">@*android:color/highlighted_text_material</item>

    <item name="android:textColorAlertDialogListItem">@*android:color/white</item>
    <item name="android:textCheckMark">@*android:drawable/indicator_check_mark_dark</item>
    <item name="android:textCheckMarkInverse">@*android:drawable/indicator_check_mark_light</item>
    <item name="android:colorControlHighlight">@*android:color/ripple_material_dark</item>
    <item name="android:colorButtonNormal">@*android:color/btn_default_material_dark</item>

    <item name="android:textColor">@*android:color/white</item>
    <item name="android:textColorSecondary">@*android:color/white</item>

    <item name="android:selectableItemBackground">@*android:color/material_grey_800</item>
    </style>

3.样式中的item说明

上面这些样式中的item,特别多,各有各有配置意义,那这些item是从那里来的呢?
以textColorHighlightInverse为例:

3.1 定义textColorPrimaryInverseDisableOnly:

./frameworks/base/core/res/res/values/attrs.xml
<attr name="textColorHighlightInverse" format="reference|color" />

./frameworks/base/core/res/res/values/public.xml
<public type="attr" name="textColorHighlightInverse" id="0x0101034f" />

3.2 在framework中来使用item定义对应Theme:

frameworks\base\core\res\res\values\themes.xml

<style name="Theme">
    <item name="isLightTheme">false</item>
    <item name="textColorHighlightInverse">@color/highlighted_text_light</item>
    ......
</style>

3.3 在app应用中,我们再overlay此值:

<style name="Theme.Settings_Dark" parent="Theme.SettingsBase">
    ......
    <item name="android:textColorHighlightInverse">@*android:color/highlighted_text_material</item>
    ......
</style>

事实上,主题细节上的item把握和区分,才是这个知识点的核心。

3.4 对于我们来说,主题的item详细列表信息,主要是在文件

frameworks\base\core\res\res\values\styles.xml
frameworks\base\core\res\res\values\styles_device_defaults.xml
frameworks\base\core\res\res\values\styles_holo.xml
frameworks\base\core\res\res\values\styles_leanback.xml
frameworks\base\core\res\res\values\styles_material.xml

frameworks\base\core\res\res\values\themes.xml
frameworks\base\core\res\res\values\themes_device_defaults.xml
frameworks\base\core\res\res\values\themes_holo.xml
frameworks\base\core\res\res\values\themes_leanback.xml
frameworks\base\core\res\res\values\themes_material.xml

Android系统开发之黑白主题动态切换_android_02

在这以frameworks\base\core\res\res\values\themes.xml文件中定义的Theme(黑底白字)为例,列一些item:

<style name="Theme">
    <item name="isLightTheme">false</item>

    <item name="colorForeground">@color/bright_foreground_dark</item>
    <item name="colorForegroundInverse">@color/bright_foreground_dark_inverse</item>
    <item name="colorBackground">@color/background_dark</item>
    <item name="colorBackgroundFloating">?attr/colorBackground</item>
    <item name="colorBackgroundCacheHint">?attr/colorBackground</item>
    <item name="disabledAlpha">0.5</item>
    <item name="primaryContentAlpha">@dimen/primary_content_alpha_material_dark</item>
    <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_material_dark</item>
    <item name="backgroundDimAmount">0.6</item>
    <item name="colorError">@color/red</item>

    <!-- Text styles -->
    <item name="textAppearance">@style/TextAppearance</item>
    <item name="textAppearanceInverse">@style/TextAppearance.Inverse</item>

    <item name="textColorPrimary">@color/primary_text_dark</item>
    <item name="textColorPrimaryInverse">@color/primary_text_light</item>
    <item name="textColorPrimaryActivated">@color/primary_text_dark</item>
    <item name="textColorPrimaryDisableOnly">@color/primary_text_dark_disable_only</item>
    <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_light_disable_only</item>
    <item name="textColorPrimaryInverseNoDisable">@color/primary_text_light_nodisable</item>
    <item name="textColorPrimaryNoDisable">@color/primary_text_dark_nodisable</item>
    <item name="textColorSecondary">@color/secondary_text_dark</item>
    <item name="textColorSecondaryInverse">@color/secondary_text_light</item>
    <item name="textColorSecondaryActivated">@color/secondary_text_dark</item>
    <item name="textColorSecondaryNoDisable">@color/secondary_text_dark_nodisable</item>
    <item name="textColorSecondaryInverseNoDisable">@color/secondary_text_light_nodisable</item>
    <item name="textColorTertiary">@color/tertiary_text_dark</item>
    <item name="textColorTertiaryInverse">@color/tertiary_text_light</item>
    <item name="textColorHint">@color/hint_foreground_dark</item>
    <item name="textColorHintInverse">@color/hint_foreground_light</item>
    <item name="textColorHighlight">@color/highlighted_text_dark</item>
    <item name="textColorHighlightInverse">@color/highlighted_text_light</item>
    <item name="textColorLink">@color/link_text_dark</item>
    <item name="textColorLinkInverse">@color/link_text_light</item>
    <item name="textColorSearchUrl">@color/search_url_text</item>
    <item name="textColorAlertDialogListItem">@color/primary_text_light_disable_only</item>

    <item name="textAppearanceLarge">@style/TextAppearance.Large</item>
    <item name="textAppearanceMedium">@style/TextAppearance.Medium</item>
    <item name="textAppearanceSmall">@style/TextAppearance.Small</item>
    <item name="textAppearanceLargeInverse">@style/TextAppearance.Large.Inverse</item>
    <item name="textAppearanceMediumInverse">@style/TextAppearance.Medium.Inverse</item>
    <item name="textAppearanceSmallInverse">@style/TextAppearance.Small.Inverse</item>
    <item name="textAppearanceSearchResultTitle">@style/TextAppearance.SearchResult.Title</item>
    <item name="textAppearanceSearchResultSubtitle">@style/TextAppearance.SearchResult.Subtitle</item>

    <item name="textAppearanceEasyCorrectSuggestion">@style/TextAppearance.EasyCorrectSuggestion</item>
    <item name="textAppearanceMisspelledSuggestion">@style/TextAppearance.MisspelledSuggestion</item>
    <item name="textAppearanceAutoCorrectionSuggestion">@style/TextAppearance.AutoCorrectionSuggestion</item>

    <item name="textAppearanceButton">@style/TextAppearance.Widget.Button</item>

    <item name="editTextColor">@color/primary_text_light</item>
    <item name="editTextBackground">@drawable/edit_text</item>

    <item name="candidatesTextStyleSpans">@string/candidates_style</item>

    <item name="textCheckMark">@drawable/indicator_check_mark_dark</item>
    <item name="textCheckMarkInverse">@drawable/indicator_check_mark_light</item>

    <item name="textAppearanceLargePopupMenu">@style/TextAppearance.Widget.PopupMenu.Large</item>
    <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Widget.PopupMenu.Small</item>
    ......
</style>


方案二:各个应用中各个控件实时根据黑白主题的来更新方案

此方案主要是根据应用主题改变时,各控件(如TextView,ImageView等)来实时的更新。
此方案做为一个常规方法,主要是处理一些因为自己客制化导致不能和主题保持一致的UI界面显示效果。
此方案常规,事实上也是最有效的。


方案三:SystemUI应用的黑白主题动态切换

此应用黑白主题动态切换,采用的方案是RRO替换资源文件

1 定义SysuiDarkThemeOverlay的要替换的不同主题的资源apk

frameworks\base\packages\overlays\SysuiDarkThemeOverlay

Android系统开发之黑白主题动态切换_android_03


最核心的就是定义此apk应用overlay应用名,如(com.android.systemui):

frameworks\base\packages\overlays\SysuiDarkThemeOverlay\AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.systemui.theme.dark"
    android:versionCode="1"
    android:versionName="1.0">
    <overlay android:targetPackage="com.android.systemui" android:priority="1"/>
    <application android:label="@string/sysui_overlay_dark" android:hasCode="false"/>
</manifest>


然后在这个应用的res中客制化不同主题的资源文件。

2 在systemui应用中设置不同主题的资源apk

SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java

public class StatusBar extends SystemUI implements DemoMode,
        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
        OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
        ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
......
private IOverlayManager mOverlayManager;

        //初始化
        mOverlayManager = IOverlayManager.Stub.asInterface(
                ServiceManager.getService(Context.OVERLAY_SERVICE));

    /**
     * Switches theme from light to dark and vice-versa.
     */
    protected void updateTheme() {

        ......
        int themeMode = Settings.Secure.getInt(mContext.getContentResolver(), "theme_mode", 2);

        final boolean useDarkTheme = themeMode == Settings.Secure.THEME_MODE_DARK;


        if (isUsingDarkTheme() != useDarkTheme) {

            mUiOffloadThread.submit(() -> {
                try {
                    //这个就是切换成黑色主题
                    mOverlayManager.setEnabled("com.android.systemui.theme.dark",
                            useDarkTheme, mLockscreenUserManager.getCurrentUserId());
                } catch (RemoteException e) {
                    Log.w(TAG, "Can't change theme", e);
                }
            });
        }

}

总结

通过上面三种方案,基本上可以实现各个应用的黑白主题动态切换。
个人经验,推荐方案一,方案二。

举报

相关推荐

0 条评论