Android Themes与Styles实践

分清Style和Theme

首先我们需要知道的是Theme和Style都是定义在res/values/styles.xml的文件下,做个类比的话,style和theme就类似于web前端中的CSS样式表,用来定义控件的显示样式。

Style

style是只针对单个View的属性集合,例如对TextView定义颜色,字体大小等

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="GreenText" parent="TextAppearance.AppCompat">
<item name="android:textColor">#00FF00</item>
</style>
</resources>
1
2
3
<TextView
style="@style/GreenText"
... />

可以看到style的定义非常简单,就是在style标签下定义不同的属性item即可。

Theme

Theme可以理解为style的一种特殊类型,针对一个集合的样式属性的应用,比方说在Application,Activity或ViewGroup中使用Theme来定义这个集合下面所有UI控件的样式,Theme中定义的样式会应用到这个集合中的每一个view

1
2
3
4
5
6
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
1
2
3
4
<manifest ... >
<application android:theme="@style/AppTheme" ... >
</application>
</manifest>
1
2
3
4
5
6
<manifest ... >
<application ... >
<activity android:theme="@style/Theme.AppCompat.Light" ... >
</activity>
</application>
</manifest>

Theme与style定义是一样的,但是在使用时是通过android:theme属性来指定

###注意事项

  • 不管是style还是theme,在应用到view时,view只会相应自己拥有的属性,就是说对ImageView应用Text的style其中设置字体颜色,大小的配置是不会在ImageView上起作用的
  • 在Android 5(Api 21)或Android Support Library V22.1之后View新增了android:theme的属性,给指定View设置theme会使的View及此View的子View都会设置theme对应的样式
  • 总结Theme和Style的最大区别就是Style只针对单个View的样式设置,而Theme则针对View集合的样式设置

Style的继承及自定义

不管是Style还是Theme,自定义都建议(强制)先继承Support包下的Style,不要直接继承Framework下的style,因为Support下的Style, Google官方都做了良好的封装和向后兼容测试,继承Support包下的Style是最稳妥的

建议

1
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

禁止

1
<style name="AppTheme2" parent="android:Theme.Black"/>

Style继承与自定义方式

Style的继承与自定义主要分两种:继承第三方项目的Style(包括support或者其他开源项目),另一种就是继承自己本项目中的style

####继承Support包或第三方项目的Style

1
2
3
4
5
6
<style name="GreenText" parent="@android:style/TextAppearance">
<item name="android:textColor">#00FF00</item>
</style>
<style name="GreenText" parent="TextAppearance.AppCompat">
<item name="android:textColor">#00FF00</item>
</style>

通过parent指定父style,再通过重写item来重写相关的属性

####继承本项目中的Style

1
2
3
<style name="GreenText.Large">
<item name="android:textSize">22dp</item>
</style>

继承自己本项目中的style可以不用parent属性指定,通过ParentStyle.CustomStyle的形式来指定style名称

如果在此种方法中也使用parent,parent中指定的style优先级高于通过点操作符指定的父类style。

对于item中需要自定义的属性可以通过查看不同控件拥有的属性来自定义

Style属性层级优先级

因为控件的属性可以通过多种方式方式设置,如果不同方式同时设置必然会引起冲突,比如我在代码通过TextView.setTextSize()设置了字体大小,同时我给此TextView设置的Style也加了textSize的配置,那最终会使用谁定义的textSize呢,所以这里就有一个优先级的问题

系统默认会按如下优先级应用,优先级从高到低:

  1. 通过Span来设置TextView及TextView的子类控件属性
  2. Java或Kotlin编码来设置属性
  3. setAttributes方法来设置属性
  4. 自定义Style来设置属性
  5. 默认的Style属性
  6. 通过Theme设置批量View的属性
  7. 特定View的特定Style属性,如通过TextAppearance个TextView设置相关属性

如果你在项目中设置的style修改一直不生效,此时就需要考虑是否其他地方也设置了相关属性,把style中的属性给覆盖了

第七点的TextAppearance有必要做一下详细的解释,TextView除了通过android:style=”@style/xxx”来指定style,还可以通过textAppearance来指定一个style

1
2
3
4
<TextView
...
android:textAppearance="@android:style/TextAppearance.Material.Headline"
android:text="This text is styled via textAppearance!" />

系统通过提供textAppearance来指定字符级别的style样式,这样我们可以通过style来指定一些其他的样式

Theme自定义

当通过AndroidStudio创建一个新项目时,AndroidStudio默认会为我们创建一个继承自Support包下AppTheme

1
2
3
4
5
6
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

我们可以修改一些属性,比如颜色来改变如状态栏,底部导航或app bar等通用控件的颜色,又或者修改背景颜色

1
2
3
4
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
...
<item name="android:windowBackground">@color/activityBackground</item>
</style>

theme中可以自定义很多属性,具体可以查阅官方R.styleable.Theme,如果是要在theme中定义全局的View或具体控件的属性则需要查看其支持的xml属性配置

在Android Support Library中定义的theme(Theme.AppCompat)相关属性配置可以查看attrs.xml文件查看

如果是Support Library中theme的属性是不需要“android:”开头的,而从Framework继承来自定义的属性是需要“android:”开头

当前Support包下支持的所有Theme可以查阅如下文件themes.xml

Theme的版本兼容

随着android版本升级,可能android5.0和android 6.0在某些主题属性上会有api的改变,为了兼容版本,可以通过在res文件夹下创建指定values版本的style来解决兼容问题

1
2
res/values/styles.xml # 如果没有指定版本,默认使用此style中的定义
res/values-v21/styles.xml # 对于Api 21以上的版本会优先使用此style种的定义

举个例子,比如Android5.0(API level 21)及以上版本汇总,增加了window的切换动画,基础的 res/values/styles.xml文件中如下:

1
2
3
4
5
6
7
8
9
10
11
<resources>
<!-- base set of styles that apply to all versions -->
<style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/primaryColor</item>
<item name="colorPrimaryDark">@color/primaryTextColor</item>
<item name="colorAccent">@color/secondaryColor</item>
</style>
<!-- declare the theme name that's actually applied in the manifest file -->
<style name="AppTheme" parent="BaseAppTheme" />
</resources>

Api 21的版本res/values-v21/styles.xml

1
2
3
4
5
6
7
8
<resources>
<!-- extend the base theme to add styles available only with API level 21+ -->
<style name="AppTheme" parent="BaseAppTheme">
<item name="android:windowActivityTransitions">true</item>
<item name="android:windowEnterTransition">@android:transition/slide_right</item>
<item name="android:windowExitTransition">@android:transition/slide_left</item>
</style>
</resources>

自定义控件样式

没有一个android的控件(TextView,Button…)在Framework或者Support Library中都有一个默认的style,例如在项目中使用一个Support包中的Theme,在使用Button控件时,就会使用Widget.AppCompat.Button样式。如果我们像对Button指定不同的样式,可以如下自定义

1
2
3
<Button
style="@style/Widget.AppCompat.Button.Borderless"
... />

如果想在整个应用都替换,可以在theme中指定

1
2
3
4
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="buttonStyle">@style/Widget.AppCompat.Button.Borderless</item>
...
</style>

样式设计规范及模块化建议

为了项目开发的高效率和项目的可扩展性,一个好的设计规范可以帮助我们快速的迭代或者扩展项目,制定一个合理的UI规范显得尤为重要

1.学习Material Design中的一些设计规范,最好(强制)让设计师一起学习了解
2.制定准确语义的设计元素,如颜色不能直接给十六进制的色值,而是定义好一个描述这个颜色的语义词如primary_color_dark等等,然后把这些定义好的规范放在一个公共的文档里来维护起来
3.可以通过一些第三方如zeplin等网站来统一管理设计图及Style Guide,这样的好处是在于设计师和工程师能及时的同步相关设计资源的改动及更新
4.在项目开发UI功能时,如果发现两个控件是一摸一样的样式,此时建议可以通过style抽取样式,这样做的好处是能统一管理及其他模块可能会复用。

总结

这几个问题你会吗?

  1. Styles和Themes的区别?
  2. 如何制作通用的styles或者Themes?
  3. 项目如何统一管理Style与Theme?
写得好,就打赏一下吧!