在android應用程序中咱們可能須要切換模式,如晚上切換到夜間模式便於閱讀等。本文參考了網上的一些資料,並結合實例,實現了動態更改主題的效果。android
Android中實現theme主題能夠使用在activity中使用setTheme(int)的方法,SDK中對此方法的說明爲:app
//Set the base theme for this context. Note that this should be called before any views are instantiated in the Context (for example before calling android.app.Activity.setContentView or android.view.LayoutInflater.inflate). //須要在setcontentview函數或者inflate函數以前使用。
效果圖以下:ide
實現步驟:函數
首先須要定義一個屬性,此屬性用於賦值給控件的屬性,至關於控件屬性值的「變量」。佈局
在attrs.xml中,定義三個屬性,屬性的format均爲reference|color學習
<resources> <attr name="button_bg" format="reference|color" /> <attr name="activity_bg" format="reference|color" /> <attr name="text_cl" format="reference|color" /> </resources>
接下來,在styles.xml中,編寫自定義的Themethis
<style name="AppBaseTheme" parent="android:Theme.Light"> </style> <style name="AppTheme" parent="AppBaseTheme"> <item name="text_cl">#ffffff</item> <item name="button_bg">#000000</item> <item name="activity_bg">#ffffff</item> </style> <style name="DarkTheme" parent="AppBaseTheme"> <item name="text_cl">#000000</item> <item name="button_bg">#ffffff</item> <item name="activity_bg">#000000</item> </style>
選擇一種模式做爲程序的默認theme,注意:因爲我是在layout佈局文件中定義的view的樣式,所以,爲了保證theme切換時不會出現找不到資源的問題,所以須要在每一種用到的自定義theme中,都加上item。這裏的item如text_cl和view的textColor屬性的format是一致的。spa
Android manifest文件:code
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testandroidsettheme" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <application android:allowBackup="true" android:name="com.example.testandroidsettheme.app.MyApp" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.testandroidsettheme.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
我將android:theme="@style/AppTheme"做爲默認的樣式。orm
主界面layout佈局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?activity_bg" android:gravity="center" android:orientation="vertical" > <Button android:id="@+id/button0" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="?button_bg" android:textColor="?text_cl" android:text="set theme" /> </LinearLayout>
在佈局文件中,button的background屬性採用的是當前theme下的button_bg的屬性值,外部的linearlayout採用的是當前theme下的activity_bg的屬性值,在填寫此屬性值時,須要在前面添加」?」,表示這是一個style中的變量。
在須要切換顯示不一樣theme的activity中,一些博客中在button點擊事件中使用
setTheme(int);
recreate();
的方式,我發現此種方式沒法實現theme的切換,由於recreate()方法會從新建立此activity,以前的setTheme()無效。個人實現方式以下:
public class MainActivity extends Activity { public Button button0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MyApp app = (MyApp)MainActivity.this.getApplication(); if(app.theme == 0){ //使用默認主題 }else{ //使用自定義的主題 setTheme(app.theme); } setContentView(R.layout.activity_main); button0 = (Button) this.findViewById(R.id.button0); button0.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MyApp app = (MyApp)MainActivity.this.getApplication(); app.theme = R.style.DarkTheme; recreate(); } }); } } public class MyApp extends Application{ public int theme = 0; }
在此activityoncreate()中的setContentView()方法調用以前,判斷當前的theme,並調用setTheme(),實現改變theme的效果。
注意:這種方法實現切換theme不是很友好,由於在activity從新建立時,可能會有閃屏的現象。比較好的解決方案如知乎客戶端採用的是截屏,並漸隱圖片的過分方式,具體的實現還有待學習。
參考資料:http://www.kymjs.com/code/2015/05/26/01/