不閃屏切換Android App主題

最近想給自己的一個鬧鐘App增加一個夜間模式,一個比較簡便的切換主題的方式就是在Styles.xml中設(shè)置兩套Theme,分別是白天模式的主題Theme,還有一個是夜間模式的Theme。然后,通過在該Activity中的setContentView()方法之前,使用setTheme(...)方法設(shè)置Activity的Theme。但是重新設(shè)置的主題Theme必須調(diào)用recreate()方法使得Activity重新調(diào)用onCreate()方法才能表現(xiàn)出來。因此,這個時候會閃屏。我們先詳細(xì)介紹下這種方式的實現(xiàn),后面再說解決閃屏的方法。

第一步,確認(rèn)哪些屬性是需要根據(jù)主題變化而改變的

以下面這個活動為例,我希望在點擊Change Theme按鈕后,可以改變

  • StatusBar的顏色
  • ToolBar的顏色
  • 整個Activity的背景顏色
  • 以及“Hello Theme”這個TextView的背景顏色
    在以上打算隨著主題Theme修改的屬性中,前三個屬性是可以直接在Theme的style.xml文件中設(shè)置的,最后一個TextView的背景顏色是需要首先自定義一個屬性,然后才能夠在style.xml文件中設(shè)置的
3.JPG

第二步自定義屬性

在values文件夾下,新建attrs.xml文件。下面的代碼代表這新建一個名為"Text_bg_Color"的屬性,該屬性值是color類型或者reference引用類型。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="Text_bg_Color" format="color|reference"/>
</resources>

第三步定義不同的主題風(fēng)格

在styles.xml中定義不同Theme,以便后面進(jìn)行切換。下面分別定義了兩個Theme的style。第一個是默認(rèn)的主題,也就是白天模式。第二個是夜間模式。兩個模式都是繼承了"Theme.AppCompat.Light.NoActionBar",所以絕大多屬性都是一樣的,不同的是分別自定義了一些屬性。

  • colorPrimary是Toolbar的背景顏色
  • colorPrimaryDark是StatusBar的顏色
  • android:textColorPrimary是主標(biāo)題的字體顏色
  • android:colorControlNormal是控制元件的默認(rèn)狀態(tài)顏色以及overflow menu(三個點)的顏色
  • colorAccent是控制元件在選中狀態(tài)的顏色
  • android:windowBackground是Activity的背景顏色
<resources
    >

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"
        >
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorLight</item>
        <item name="colorPrimaryDark">@color/colorLight</item>
        <item name="android:textColorPrimary">@android:color/black</item>
        <item name="android:colorControlNormal">@android:color/white</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowBackground">@android:color/white</item>
        <item name="TextView_bg_Color">@android:color/white</item>
    </style>

    <style name="AppThemeNight" parent="Theme.AppCompat.Light.NoActionBar"
        >
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorNight</item>
        <item name="colorPrimaryDark">@color/colorNight</item>
        <item name="android:textColorPrimary">@android:color/black</item>
        <item name="android:colorControlNormal">@android:color/white</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowBackground">@android:color/black</item>
        <item name="TextView_bg_Color">@color/colorGray</item>
    </style>
</resources>

在布局文件中,TextView要使用對應(yīng)style中的TextView_bg_Color屬性。"?attr/TextView_bg_Color"代表使用自定義屬性TextView_bg_Color的值,而該屬性的已經(jīng)唄Theme文件所定義。所以,TextView的背景顏色就被Theme所定義了。

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/Theme_Button"
        android:background="?attr/TextView_bg_Color"
        android:id="@+id/text_view"
        android:text="Hello Theme"/>

第四步在Activity中切換主題Theme

自定義一個ThemeUtile類,這是一個幫助切換主題的工具類。這個類提供了一個public static的布爾型的變量night,用來記錄整個App所處于的主題模式。這個類還提供了一個public static方法changeTheme(),根據(jù)night的值,來對所有的Activity設(shè)置主題。

public class ThemeUtile {
    public static boolean night = false;
    public static void changeTheme(Activity activity){
        if (ThemeUtile.night){
            activity.setTheme(R.style.AppThemeNight);
        }else{
            activity.setTheme(R.style.AppTheme);
        }
    }
}

接著設(shè)置切換主題Button的點擊事件。通過設(shè)置不同的night值,來設(shè)置不同的主題模式。因為setTheme()方法必須要在setContentView()方法之前調(diào)用,所以為了使當(dāng)前Activity的主題切換成功,需要調(diào)用recreate()方法來重新調(diào)用onCreate()方法。這樣也導(dǎo)致了當(dāng)前Activity被銷毀,并重新啟動,所以會出現(xiàn)閃屏的現(xiàn)象。

        Button button = (Button) findViewById(R.id.Theme_Button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (ThemeUtile.night) {
                    ThemeUtile.night = false;
                } else {
                    ThemeUtile.night = true;
                }
                recreate();
            }
        });

夜間模式效果如下。


night.JPG

第五步 解決閃屏的問題

我搜索了很多解決不閃屏切換主題的方法,要么效果不太好,要么比較“難”,需要較深的知識積累,我就想了一個比較取巧的方法,但是沒有那么優(yōu)雅。由于在當(dāng)前屏幕值重新設(shè)置主題,會導(dǎo)致重新調(diào)用onCreate()方法導(dǎo)致閃屏。我們可以在一個新開的Activity中通過ThemeUtile.night變量重新設(shè)置主題,但不調(diào)用recreate()方法切換主題,而是“手動”設(shè)置需要改變的屬性,在退出該Activity時,使用Intent回到之前的界面,并 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);,使之前的Activity全部出棧,重新創(chuàng)建一個新的Activity,執(zhí)行onCreate()方法,從而改變主題。
代碼如下:

//在onCreate()方法中
ThemeUtile.changeTheme(this);
setContentView(R.layout.second_activity);
super.onCreate(savedInstanceState);
        ...
//設(shè)置改變主題按鈕的點擊事件
button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (! ThemeUtile.night) {
                    findViewById(R.id.sec_activity).setBackgroundColor(getResources().getColor(R.color.colorGray));
                    getWindow().setStatusBarColor(getResources().getColor(R.color.colorGray));
                    toolbar.setBackgroundColor(getResources().getColor(R.color.colorGray));
                    ThemeUtile.night = true;
                }else
                {
                    findViewById(R.id.sec_activity).setBackgroundColor(getResources().getColor(R.color.colorWhite));
                    getWindow().setStatusBarColor(getResources().getColor(R.color.colorLight));
                    toolbar.setBackgroundColor(getResources().getColor(R.color.colorLight));
                    ThemeUtile.night = false;
                }
            }
        });


//重寫onBackPressed()方法
    @Override
    public void onBackPressed() {
        Intent intent = new Intent(SecondActivity.this,MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
        super.onBackPressed();
    }

就是這樣,雖然不是很優(yōu)雅,但是完成了不閃屏切換Android App主題。

2016.06.03更新:
注意,setTheme()方法必須在setContentView()super.onCreate()之前調(diào)用,否則,Theme中的某些屬性將無法顯示出來。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容