Android应用中动态更改主题的实现
在android应用程序中我们可能需要切换模式,如晚上切换到夜间模式便于阅读等。本文参考了网上的一些资料,并结合实例,实现了动态更改主题的效果。
Android中实现theme主题可以使用在activity中使用setTheme(int)的方法,SDK中对此方法的说明为:
//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函数之前使用。
效果图如下:
实现步骤:
首先需要定义一个属性,此属性用于赋值给控件的属性,相当于控件属性值的“变量”。
在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中,编写自定义的Theme
<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是一致的。
Android manifest文件:
<?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"作为默认的样式。
主界面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/