Android主題切換之探究白天和夜間模式
智能手機(jī)的迅速普及,大大的豐富了我們的娛樂生活。現(xiàn)在大家都喜歡晚上睡覺前玩會(huì)兒手機(jī),但是應(yīng)用的日間模式往往亮度太大,對(duì)眼睛有較為嚴(yán)重的傷害。因此,如今的應(yīng)用往往開發(fā)了 日間和夜間 兩種模式供用戶切換使用,那日間和夜間模式切換究竟是怎樣實(shí)現(xiàn)的呢?
在文字類的App上面基本上都會(huì)涉及到夜間模式、就是能夠根據(jù)不同的設(shè)定、呈現(xiàn)不同風(fēng)格的界面給用戶、而且晚上看著不傷眼睛、實(shí)現(xiàn)方式也就是所謂的換膚(主題切換)、對(duì)于夜間模式的實(shí)現(xiàn)網(wǎng)上流傳了很多種方式、這里先分享一個(gè)方法給大家、通過設(shè)置背景為透明的方法、降低屏幕的亮度與色度。

夜間模式代碼
public void night() {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity=Gravity.BOTTOM;
params.y=10;
if(myView==null){
myView=new TextView(this);
myView.setBackgroundColor(0x80000000);
}
mWindowManager.addView(myView, params);
Editor edit = skinSp.edit();
edit.putString("skin", NIGHT);
edit.commit();
}
白天模式
public void day(){
if(myView!=null){
mWindowManager.removeView(myView);
Editor edit = skinSp.edit();
edit.putString("skin", DAY);
edit.commit();
}
}
下面通過實(shí)例操作來詳細(xì)展示如何進(jìn)行Android主題切換中的白天/夜間模式。


上述兩幅圖片,正是兩款A(yù)pp的夜間模式效果,所以,依據(jù)這個(gè)功能,來看看切換主題到底是怎么實(shí)現(xiàn)的(當(dāng)然現(xiàn)在github有好多PluginTheme開源插件,很多時(shí)候可以使用這些插件,不過我并不想講怎么用那些插件,正所謂會(huì)用輪子還不如會(huì)造輪子)。
關(guān)于更換主題和換膚
這里提到是做換主題功能,當(dāng)然與之類似的就是換膚,換膚現(xiàn)在比較流行的是采用插件化動(dòng)態(tài)加載技術(shù)來實(shí)現(xiàn)的,這樣可以起到熱插拔作用,需要皮膚時(shí)候用戶自主的在網(wǎng)上下載便是了,不用皮膚時(shí)便刪了皮膚插件包而不會(huì)影響宿主App的功能,這樣就不必把一大堆皮膚圖片放在本地而增加apk的大小,關(guān)于用插件化實(shí)現(xiàn)換膚功能這僅僅是插件化技術(shù)的冰山一角,關(guān)于插件化技術(shù)更多的作用,可以看看360前兩天開源的 DroidPlugin插件框架、OpenAltas框架、還有主席的DL框架。
好了,言歸正傳,現(xiàn)在我們需要實(shí)現(xiàn)的是主題切換功能,關(guān)于主題切換其實(shí)是切換整個(gè)App的顏色風(fēng)格、排版風(fēng)格、字體風(fēng)格等,其中并不會(huì)有過多的圖片資源的切換,如有過多的圖片的更換那就是換膚的功能了。
現(xiàn)在我們要實(shí)現(xiàn)夜間/白天模式的切換功能,如下效果圖:

可以看到上面的效果正是夜間和白天兩種模式的切換功能,切換至夜間模式時(shí)整個(gè)App的背景色、字體顏色、按鈕顏色、標(biāo)題欄顏色等全部需要切為夜間模式的顏色,當(dāng)切回白天模式又切回原來的顏色,來看看怎么做的?
實(shí)現(xiàn)主題切換
首先就是需要在app中準(zhǔn)備兩套主題:
白天主題
<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>
夜間主題
<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>
上面這兩套主題中,各個(gè)屬性定義完全一模一樣,不一樣的只是屬性的值,其中在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中是不存在這些屬性的,它們這些是自定義屬性,主要是用來控制某些控件或者布局的屬性,它們的定義在attr文件中:
<?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>
然后在布局中引用即可:
<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="設(shè)置"
android:background="?attr/buttonBg"
android:textColor="?attr/buttonTextColor" />
</LinearLayout>
最后在整個(gè)App主題的style中使用它們就ok了。這樣做有什么好處呢?我們都知道App設(shè)置主題時(shí)候都是設(shè)置一個(gè)style,而App中某些控件或者布局的背景或者style樣式需要和整個(gè)主題樣式不同時(shí),這時(shí)候可以通過設(shè)置個(gè)自定義屬性,通過在App的style中給與自定義屬性不同的值來達(dá)到目的。
切換主題
好了,有了兩套主題了,接下來是通過代碼來進(jìn)行控制主題間的切換了,控制主題的切換其實(shí)就是通過setTheme(R.style.*);來設(shè)置不同的style從而達(dá)到界面風(fēng)格的變換,不過這個(gè)方法setTheme()只在setContentView()方法前設(shè)置才有效,所以如果你想在其它地方調(diào)用這個(gè)方法來切換主題那是肯定不行的,所以這里有兩個(gè)難點(diǎn)?
1、怎么處理當(dāng)前的設(shè)置界面在切換主題后同時(shí)切換主題風(fēng)格
2、怎么處理之前已經(jīng)打開的界面讓他們切換主題風(fēng)格
這里我給出的答案是:
1、在當(dāng)前切換主題的設(shè)置界面使用Activity.recreate()方法,該方法的作用就是當(dāng)當(dāng)前Activity的配置發(fā)生變化時(shí),調(diào)用這個(gè)方法可以把當(dāng)前Activity實(shí)例銷毀并重新創(chuàng)建出一個(gè)Activity實(shí)例。如此可見通過這個(gè)方法可以很容易的解決問題一,因?yàn)樗鼤?huì)重新創(chuàng)建一個(gè)新的Activity實(shí)例。
2、這里我使用的方法是通過設(shè)置Intent的Flag來達(dá)到更新之前Activity的效果,通過設(shè)置mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);讓它清除之前的Activity再創(chuàng)建一個(gè)新的Activity,這樣當(dāng)返回之前的界面就可以更新主題了?!咀ⅰ咳绻卸鄠€(gè)界面可以通過設(shè)置主界面MainActivity的launchMode為singleTask,在返回主界面時(shí)候清除其它界面來更新主題
對(duì)于上面的方法(如有更好的方法歡迎告知,萬分感謝?。?/p>
代碼實(shí)現(xiàn)
最后再貼下代碼:
通過一個(gè)主題設(shè)置工具類設(shè)置主題,在每個(gè)Activity的setContentView()方法之前設(shè)置主題:
設(shè)置主題工具類:
public class ThemeChangeUtil {
public static boolean isChange = false;
public static void changeTheme(Activity activity){
if(isChange){
activity.setTheme(R.style.NightTheme);
}
}
}
設(shè)置界面:
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();//重新創(chuàng)建當(dāng)前Activity實(shí)例
}
});
}
@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();
}
}
主界面:
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));
}
});
}
}
以上就是Android主題切換中的白天/夜間模式的詳細(xì)過程及代碼,一開始先給大家簡單的展示了代碼,而后詳細(xì)的介紹過程及代碼,需要的朋友參考。
相關(guān)文章
Android ListView的Item點(diǎn)擊效果的定制
這篇文章主要介紹了Android ListView的Item點(diǎn)擊效果的定制的相關(guān)資料,需要的朋友可以參考下2017-07-07
android自定義控件ImageView實(shí)現(xiàn)圓形圖片
這篇文章主要為大家詳細(xì)介紹了android自定義控件ImageView實(shí)現(xiàn)圓形圖片,適用于用戶頭像,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android多進(jìn)程間采用AIDL方式進(jìn)行通信
這篇文章主要為大家詳細(xì)介紹了Android多進(jìn)程間采用AIDL方式進(jìn)行通信,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04
解決Android Studio Log.v和Log.d不顯示的問題
這篇文章主要介紹了解決Android Studio Log.v和Log.d不顯示的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08
android Bitmap圓角與倒影的具體實(shí)現(xiàn)代碼
android Bitmap圓角與倒影的具體實(shí)現(xiàn)代碼,需要的朋友可以參考一下2013-06-06
關(guān)于Android SDCard存儲(chǔ)的問題
本篇文章小編為大家介紹,關(guān)于Android SDCard存儲(chǔ)的問題。需要的朋友參考下2013-04-04
Android Volley擴(kuò)展實(shí)現(xiàn)支持進(jìn)度條的文件上傳功能
這篇文章主要為大家詳細(xì)介紹了Android Volley擴(kuò)展實(shí)現(xiàn)文件上傳與下載功能,支持進(jìn)度條,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
Android 將view 轉(zhuǎn)換為Bitmap出現(xiàn)空指針問題解決辦法
這篇文章主要介紹了Android 將view 轉(zhuǎn)換為Bitmap出現(xiàn)空指針問題解決辦法的相關(guān)資料,這里提供實(shí)例并提供解決辦法,需要的朋友可以參考下2017-07-07

