Android實現日夜間模式的三種常用方法(一)
1、使用 setTheme
的方法讓 Activity
重新設定主題;
2、設定 Android
Support Library
中的 UiMode
來支援日間/夜間模式的切換;
3、通過資源 id 對映,回撥自定義 ThemeChangeListener
介面來處理日間/夜間模式的切換。
一、使用 setTheme 方法
我們先來看看使用 setTheme
方法來實現日間/夜間模式切換的方案。這種方案的思路很簡單,就是在使用者選擇夜間模式時,Activity
設定成夜間模式的主題,之後再讓 Activity
呼叫 recreate()
方法重新建立一遍就行了。
那就動手吧,在 colors.xml 中定義兩組顏色,分別表示日間和夜間的主題色:
之後在 styles.xml 中定義兩組主題,也就是日間主題和夜間主題:<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> <color name="colorAccent">#FF4081</color> <color name="nightColorPrimary">#3b3b3b</color> <color name="nightColorPrimaryDark">#383838</color> <color name="nightColorAccent">#a72b55</color> </resources>
在主題中的<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="android:textColor">@android:color/black</item> <item name="mainBackground">@android:color/white</item> </style> <style name="NightAppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/nightColorPrimary</item> <item name="colorPrimaryDark">@color/nightColorPrimaryDark</item> <item name="colorAccent">@color/nightColorAccent</item> <item name="android:textColor">@android:color/white</item> <item name="mainBackground">@color/nightColorPrimaryDark</item> </style> </resources>
mainBackground
屬性是我們自定義的屬性,用來表示背景色:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="mainBackground" format="color|reference"></attr>
</resources>
接下來就是看一下佈局 activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/mainBackground"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.yuqirong.themedemo.MainActivity">
<Button
android:id="@+id/btn_theme"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="切換日/夜間模式" />
<TextView
android:id="@+id/tv"
android:layout_below="@id/btn_theme"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="通過setTheme()的方法" />
</RelativeLayout>
在 <RelativeLayout> 的 android:background
屬性中,我們使用
"?attr/mainBackground" 來表示,這樣就代表著 RelativeLayout
的背景色會去引用在主題中事先定義好的 mainBackground
屬性的值。這樣就實現了日間/夜間模式切換的換色了。
最後就是 MainActivity
的程式碼:
public class MainActivity extends AppCompatActivity {
// 預設是日間模式
private int theme = R.style.AppTheme;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 判斷是否有主題儲存
if(savedInstanceState != null){
theme = savedInstanceState.getInt("theme");
setTheme(theme);
}
setContentView(R.layout.activity_main);
Button btn_theme = (Button) findViewById(R.id.btn_theme);
btn_theme.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
theme = (theme == R.style.AppTheme) ? R.style.NightAppTheme : R.style.AppTheme;
MainActivity.this.recreate();
}
});
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("theme", theme);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
theme = savedInstanceState.getInt("theme");
}
}
在 MainActivity
中有幾點要注意一下:
1、呼叫 recreate()
方法後
Activity 的生命週期會呼叫 onSaveInstanceState(Bundle
outState)
來備份相關的資料,之後也會呼叫 onRestoreInstanceState(Bundle
savedInstanceState)
來還原相關的資料,因此我們把 theme
的值儲存進去,以便
Activity 重新建立後使用。
2、我們在 onCreate(Bundle
savedInstanceState)
方法中還原得到了 theme
值後,setTheme()
方法一定要在 setContentView()
方法之前呼叫,否則的話就看不到效果了。
3、recreate()
方法是在
API 11 中新增進來的,所以在 Android 2.X 中使用會拋異常。
貼完上面的程式碼之後,我們來看一下該方案實現的效果圖: