开源地址:http://git.oschina.net/ivigiles/ThemeSwitch
系统版本:target=android-17
1.自定义属性准备
在 /res/values/ 中创建一个attrs.xml文件,类型为resources。内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="UTF-8"?> <resources> <!-- 声明自定义属性,名称为myStyle --> <declare-styleable name="myStyle"> <!-- 如果此属性用于图片,就对应这个attr。名称mypic,类型reference为引用/res下的资源 --> <attr name="mypic" format="reference"/> <!-- 如果此属性用于颜色,就对应这个attr。名称mycolor,类型color为16进制颜色值 --> <attr name="mycolor" format="color"/> </declare-styleable> </resources> |
2.自定义Style准备
在 /res/values/style.xml 里新建2个style节点,用于白天和夜晚主题匹配。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!-- 白天主题模式 --> <style name="day"> <!-- 这个mypic对应attrs.xml里的mypic --> <item name="mypic">@drawable/day</item> <!-- 节点的value即reference,引用的图片资源 --> <!-- 此mycolor对应attrs.xml里定义的颜色属性 --> <item name="mycolor">#990066</item> </style> <!-- 夜晚主题模式 --> <style name="night"> <item name="mypic">@drawable/night</item> <!-- 在/res/drawable/中准备对应的2张图片 --> <item name="mycolor">#ff00cc</item> </style> </resources> |
3.定义主题管理工具类及主题切换接口
关键类,其中有一个标识属性,保存当前主题标识。还有个容器属性,用来存储全部需要动态设置主题的接口。
主题切换监听接口,可以是内部public类,也可以是独立的类,随意。此接口定义一个回调方法,用来操作Aty中的控件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
/** * 主题管理工具类<br/> * 单例 */ public class ThemeLogic { /** 工具类 */ private static ThemeLogic instance; /** 当前模式。1:白天,2:夜晚 */ public static int themeType = 1; /** 要切换模式的监听器-Aty */ private List<ThemeChangeListener> themeList = new ArrayList<ThemeLogic.ThemeChangeListener>(); // 私有构造函数,避免实例化 private ThemeLogic() { } /** * 获取工具类实例 * @return 工具类实例 */ public static ThemeLogic getInstance() { // 懒汉模式 if (instance == null) { instance = new ThemeLogic(); } return instance; } /** * 添加主题切换监听器 * @param listener 实现监听器的Aty */ public void addListener(ThemeChangeListener listener) { themeList.add(listener); } /** * 移除主题切换监听器 * @param listener 实现监听器的Aty */ public void removeListener(ThemeChangeListener listener) { themeList.remove(listener); } /** * 执行切换 */ public void notifiyChange() { for (ThemeChangeListener themeChangeListener : themeList) { themeChangeListener.onThemeChanged(); } } /** * 主题切换监听器。此接口可单独定义<br/> * 任何需要切换主题的Activity都有实现此接口,以回调的形式实现切换 */ public interface ThemeChangeListener { /** * 切换主题功能 */ void onThemeChanged(); } } |
4.使用自定义Style
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<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" > <!-- 每个需要切换主题的控件,都引用自定义的属性 形式为以问号开头attr下的自定义属性名称 --> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" android:textColor="?attr/mycolor" /> <ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="?attr/mypic" /> </RelativeLayout> |
5.Activity实现主题切换监听
每个需要主题切换功能的Aty都实现Theme切换监听接口,实现其切换功能。在这个功能里首先set新的主题,然后加载新的属性,最后更新控件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
/** * 需动态切换主题的Aty都实现监听器 */ public class MainActivity extends Activity implements ThemeChangeListener { private TextView tv; private ImageView img; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /** 1。!!! 在setContentView之前setTheme !!! */ setTheme(R.style.day); /** 2。设置布局 */ setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); img = (ImageView) findViewById(R.id.img); /** 3。备案 */ ThemeLogic.getInstance().addListener(this); startActivity(new Intent(MainActivity.this, ThemeActivity.class));// 多个Aty测试 } /** * 4。当执行切换时,回调此方法,改变控件可视状态 */ @Override public void onThemeChanged() { // 匹配目标主题 switch (ThemeLogic.themeType) { case 1: // 1. 更新主题 setTheme(R.style.day); break; case 2: setTheme(R.style.night); break; } // 2. 加载自定义属性 TypedArray typedArray = obtainStyledAttributes(R.styleable.myStyle); // 3. 更新控件 tv.setTextColor(typedArray.getColor(R.styleable.myStyle_mycolor, Color.BLACK)); img.setImageDrawable(typedArray.getDrawable(R.styleable.myStyle_mypic)); // 释放资源 typedArray.recycle(); } } |
6.动态切换
多数App都有自动切换主题的功能。在早晨及傍晚,会根据时间自动切换白天模式和夜晚模式,其为定时器监听系统模式切换或由时钟判断时间,然后以广播的形式通知Activity进行切换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 按钮事件触发主题切换 @Override public void onClick(View v) { switch (v.getId()) { case R.id.day: ThemeLogic.themeType=1; // 首先切换主题标识 ThemeLogic.getInstance().notifiyChange(); // 然后执行监听器的切换功能 break; case R.id.night: ThemeLogic.themeType=2; ThemeLogic.getInstance().notifiyChange(); break; } } |
7.注意事项
一般的横竖屏切换事件处理时,一直兼容到3.2,通常在AM.xml的Activity节点中设置属性:
1 2 3 |
android:configChanges="orientation|keyboardHidden|screenSize" |
这样当前显示的Activity就不会重xin执行onCreate进行重建。但在NEXUS5+6.0中测试,仍然重走onCreate,致使切换屏幕方向之前的主题失效。
因此,针对本例续上述代码,提供解决方法,在要执行主题切换的Aty的onCreate中,对setTheme语句进行增强:
1 2 3 4 5 6 7 8 9 10 |
switch(ThemeLogic.themeType){ case 1: setTheme(R.style.day); break; case 2: setTheme(R.style.night); break; } |
本文由崔维友 威格灵 cuiweiyou vigiles cuiweiyou 原创,转载请注明出处:http://www.gaohaiyan.com/1790.html
承接App定制、企业web站点、办公系统软件 设计开发,外包项目,毕设