android theme 使用实践
Contents
Android中的theme和css有点像,界面容器和样式是解耦的。在Android中,theme如果声明在application中,是对整个application有效,声明在activity中则只对当前activity有效。
我使用theme的背景需求是:不同的接入方需要使用我们的SDK,然而他们都有各自的主题色,各自的选择框、progressbar等样式。
我的解决方案是将这些不同的需要设置的地方提取为attr,单独移到一个theme文件中。在SDK中提供一份默认theme,接入方如有需求,只需要按照自定义界面需求修改对应的一份同名theme。具体如下:
也许是设置theme的最佳姿势[1]
一、添加如下几个文件
<!--attr.xml--> |
<!--theme.xml--> |
使用方式
<Button |
注意:并不能在dialog_positive_btn_bg.xml
的drawable里面直接使用?attr/*
,在Android sdk 21 之前这样做会弹异常[2]。所以只能在theme中用?attr/*
引用一份drawable的res,也就是每一个theme都会对应有一份drawable文件。(会导致drawable文件大量重复,虽然只有color等不一样)
二、调用方式
2.1 在BaseAct 中设置
@Override |
Dialog这种attach到activity的, 直接在dialog的content layout xml文件中使用?attr/btn_positive_bg
无效果。Dialog不能调用setTheme, 当其构造函数传入的context为baseAct的子类时能如下这样手动设置。当然其他能通过context取到theme的地方也可以这样调用。
TypedValue typedValue = new TypedValue(); |
在TextView中的某个代码片段
final Resources.Theme theme = context.getTheme(); |
2.2 在manifest 的application,activity中设置
<application |
or
<activity |
这两者的作用是有顺序的,看代码便知:
//ActivityThread.java-performLaunchActivity() |
三、Dialog、以及已经有了自定义的theme的activity
已经存在自定义theme的activity,及dialog的theme如何setting?
让自定义的theme继承于要设置的theme。然后对于activitity调用setTheme,对于dialog调用
public Dialog(Context context, int theme) { |
四、theme的调用顺序
- 对于在manifest.xml中定义的theme,写在application和activity中的会二选一,如果activity有设置theme,则application中设置的会忽略。
- 对于在onCreate中调用的setTheme,则相当于在manifest ActivityTheme / manifest ApplictionTheme 之后又调用了一遍setTheme 。这里有个坑:
<!-- application theme in manifest--> |
即:同时设置了application的theme,设置了windows的相关属性后,如窗体背景色,又在activity中调用setTheme()去覆盖windows的属性,如设置窗体透明,运行会发现setTheme设置的背景透明效果无效,窗体背景变成黑色的了。
结论:如果涉及到window的某些属性,建议写到xml中,不要在setTheme中修改,毕竟xml的优先级更高。重复设置window属性可能会有冲突(原因未知)。
五、theme的继承方式和顺序的区别
-
parent关键字继承
<style name="GreenText" parent="@android:style/TextAppearance">
<item name="android:textColor">#00FF00</item>
</style> -
.
的方式继承<style name="CodeFont.Red">
<item name="android:textColor">#FF0000</item>
</style>
区别:
Note: This technique for inheritance by chaining together names only works for styles defined by your own resources. You can’t inherit Android built-in styles this way. To reference a built-in style, such as TextAppearance, you must use the parent attribute.
注意:这种将theme名字以点链接起来的方式,只适用于你自己定义的styles,对于Android内置的style,则无法work。要想继承内置的style,必须使用parent属性的方式继承。
(注意:在我的实际需求中,SDK被以aar的形式调用,自己本身在theme.xml中写了一份主题作为默认主题,调用方在自己的那份theme.xml中按照需要去覆盖要修改的attr,这里即使某些attr与我们默认提供的一样,也还是得声明一遍。如果使用点继承的方式去theme的话,你永远只能拿到默认的attr,而调用方设置的attr永远不会生效,但是转为parent继承这份theme后则无此问题,说明内部实现对两种继承方式的实现相当于全局变量和局部变量。)
Author: deskid
Link: https://deskid.github.io/2015/07/28/android_theme_的正确使用方式/
License: 知识共享署名-非商业性使用 4.0 国际许可协议