1. 程式人生 > >Android實現日夜間模式的三種常用方法(一)

Android實現日夜間模式的三種常用方法(一)

 1、使用 setTheme 的方法讓 Activity 重新設定主題;

 2、設定 Android Support Library 中的 UiMode 來支援日間/夜間模式的切換;

 3、通過資源 id 對映,回撥自定義 ThemeChangeListener 介面來處理日間/夜間模式的切換。

一、使用 setTheme 方法

我們先來看看使用 setTheme 方法來實現日間/夜間模式切換的方案。這種方案的思路很簡單,就是在使用者選擇夜間模式時,Activity 設定成夜間模式的主題,之後再讓 Activity 呼叫 recreate() 方法重新建立一遍就行了。

那就動手吧,在 colors.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>
之後在 styles.xml 中定義兩組主題,也就是日間主題和夜間主題:
<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 中使用會拋異常。

貼完上面的程式碼之後,我們來看一下該方案實現的效果圖: