白天和黑夜主题切换
实现效果
首先就是需要在app中准备两套主题:
白天主题
在values文件夹中创建styles.xml
<resources> <style name="DayTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">#03A9F4</item> <item name="android:textColorPrimary">#ffffff</item> <item name="android:windowBackground">@color/background_material_light</item> <item name="colorAccent">#00BCD4</item> <item name="colorControlNormal">#00BCD4</item> <item name="titleStyle">@style/DayTitleStyle</item> <item name="contentStyle">@style/DayContentStyle</item> <item name="buttonBg">#2196F3</item> <item name="buttonTextColor">#ffffff</item> <item name="checkTextColor">#2196F3</item> <item name="switchTextColor">#2196F3</item> </style> <style name="DayTitleStyle"> <item name="android:textColor">#212121</item> <item name="android:textSize">20sp</item> <item name="android:layout_margin">8dp</item> </style> <style name="DayContentStyle"> <item name="android:textColor">#9C27B0</item> <item name="android:textSize">16sp</item> <item name="android:layout_margin">16dp</item> <item name="android:maxLines">10</item> </style> </resources>
夜间主题
在values文件夹中创建styles2.xml
<resources> <style name="NightTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">#00796B</item> <item name="android:textColorPrimary">#212121</item> <item name="android:windowBackground">@color/background_material_dark</item> <item name="colorAccent">#00796B</item> <item name="colorControlNormal">#212121</item> <item name="titleStyle">@style/NightTitleStyle</item> <item name="contentStyle">@style/NightContentStyle</item> <item name="buttonBg">#00796B</item> <item name="buttonTextColor">#9E9E9E</item> <item name="checkTextColor">#212121</item> <item name="switchTextColor">#212121</item> </style> <style name="NightTitleStyle"> <item name="android:textColor">#212121</item> <item name="android:textSize">20sp</item> <item name="android:layout_margin">8dp</item> </style> <style name="NightContentStyle"> <item name="android:textColor">#212121</item> <item name="android:textSize">16sp</item> <item name="android:layout_margin">16dp</item> <item name="android:maxLines">10</item> </style> </resources>
创建自定义属性
上面这两套主题中,各个属性定义完全一模一样,不一样的只是属性的值,其中在DayTheme和NightTheme的style中有这么一段代码:
<item name="titleStyle">@style/DayTitleStyle</item> <item name="contentStyle">@style/DayContentStyle</item> <item name="buttonBg">#2196F3</item> <item name="buttonTextColor">#ffffff</item> <item name="checkTextColor">#2196F3</item> <item name="switchTextColor">#2196F3</item>
正常情况下style中是不存在这些属性的,它们这些是自定义属性,主要是用来控制某些控件或者布局的属性,它们的定义在attrs文件中:
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="contentStyle" format="reference"/> <attr name="titleStyle" format="reference"/> <attr name="buttonBg" format="reference|color"/> <attr name="buttonTextColor" format="reference|color"/> <attr name="checkTextColor" format="reference|color"/> <attr name="switchTextColor" format="reference|color"/> </resources>
在布局文件 layout_main 中引用相应属性
<LinearLayout 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:orientation="vertical" tools:context=".MainActivity"> <TextView style="?attr/titleStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/title" /> <TextView style="?attr/contentStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="CheckBox" android:textColor="?attr/checkTextColor" /> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="CheckBox" android:textColor="?attr/checkTextColor" /> <Switch android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Switch" android:textColor="?attr/switchTextColor" /> <Button android:id="@+id/btn_setting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="设置" android:background="?attr/buttonBg" android:textColor="?attr/buttonTextColor" /> </LinearLayout>
切换主题
有了两套主题了,接下来是通过代码来进行控制主题间的切换了,控制主题的切换其实就是通过setTheme(R.style.*)来设置不同的style从而达到界面风格的变换,不过这个方法setTheme()只在setContentView()方法前设置才有效,所以如果你想在其它地方调用这个方法来切换主题那是肯定不行的,所以这里有两个难点?
1、怎么处理当前的设置界面在切换主题后同时切换主题风格
2、怎么处理之前已经打开的界面让他们切换主题风格
这里我给出的答案是:
1、在当前切换主题的设置界面使用Activity.recreate()方法,该方法的作用就是当当前Activity的配置发生变化时,调用这个方法可以把当前Activity实例销毁并重新创建出一个Activity实例。如此可见通过这个方法可以很容易的解决问题一,因为它会重新创建一个新的Activity实例。
2、这里我使用的方法是通过设置Intent的Flag来达到更新之前Activity的效果,通过设置
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
让它清除之前的Activity再创建一个新的Activity,这样当返回之前的界面就可以更新主题了。【注】如果有多个界面可以通过设置主界面MainActivity的launchMode为singleTask,在返回主界面时候清除其它界面来更新主题
清单文件中
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/DayTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".ChangeTheme"/> </application>
代码实现
通过一个主题设置工具类设置主题,在每个Activity的setContentView()方法之前设置主题:
设置主题工具类
ThemeChangeUtil:
public class ThemeChangeUtil { public static boolean isChange = false; public static void changeTheme(Activity activity){ if(isChange){ activity.setTheme(R.style.NightTheme); } } }
设置选项界面
acitvity_change.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_change" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="?attr/buttonBg" android:textColor="?attr/buttonTextColor" android:text="夜间/白天模式" /> </LinearLayout>
设置选项代码实现
ThemeChange.java
public class ChangeTheme extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { ThemeChangeUtil.changeTheme(this); super.onCreate(savedInstanceState); setContentView(R.layout.activity_change); Button mChangeBtn = (Button) findViewById(R.id.btn_change); mChangeBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (ThemeChangeUtil.isChange) { ThemeChangeUtil.isChange = false; } else { ThemeChangeUtil.isChange = true; } ChangeTheme.this.recreate();//重新创建当前Activity实例 } }); } @Override public void onBackPressed() { super.onBackPressed(); Intent mIntent = new Intent(this, MainActivity.class); mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(mIntent); finish(); } }
主界面代码实现
MainActivity.java
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { ThemeChangeUtil.changeTheme(this); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button mSettingBtn = (Button) findViewById(R.id.btn_setting); mSettingBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MainActivity.this.startActivity(new Intent(MainActivity.this, ChangeTheme.class)); } }); } }
完成切换主题