4

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

 2 months ago
source link: https://blog.51cto.com/u_16502883/9587163
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

精选 原创

hfreeman2008 2024-02-04 16:05:05 博主文章分类:android浅谈系统 ©著作权

文章标签 android 黑白主题 文章分类 软件研发 阅读数190

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

Android系统开发之黑白主题动态切换_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系统开发之黑白主题动态切换_黑白主题_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系统开发之黑白主题动态切换_黑白主题_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);
                }
            });
        }

}

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

  • 收藏
  • 评论
  • 分享
  • 举报

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK