Android的Styles(样式)和Themes(主题)非常类似Web开发里的CSS,方便开发者将页面内容和布局呈现分开。Style和Theme在Android里的定义方式是完全一样的,两者只是概念上的区别:Style作用在单个视图或控件上,而Theme用于Activity或整个应用程序。由于作用范围的不同,Theme也就需要比Style包含更多的定义属性值的项目(item)。不过本文,我将Style和Theme都归为Style来称呼。

Android的Style和Web的CSS相比,有一个缺陷就是只能针对一个对象只能通过android:theme="@style/AppTheme"style="@style/MyStyle"指定一个值。而CSS则可以通过class属性在DOM元素上定义多个样式来达到组合的效果。不过Style也有CSS没有的功能,那就是继承(Inheritance)。(当然CSS通过LESS和SASS这些工具也获得继承的能力。)

Style继承简介

根据Android Developers官方文档的介绍,定义Style的继承有两种方式:一是通过parent标志父Style;

Java
1 2 3

另一种则是将父Style的名字作为前缀,然后通过“.”连接新定义Style的名字:

Java
1 2 3

第二种方式可以无限连接子Style来实践多层继承:

Java
1 2 3

相对第一种,Android对第二种方式做出的限制就是Style必须是由自己定义的,或者说父Style和子Style必须是定义在同一个程序内,不能是引用第三方或系统的Style。毕竟对于系统的Style的引用是需要加上android:前缀作为命名空间。

其次在使用Style时,对于第二种方式定义的Style,必须引用其完全的名字,也就是说必须要包含完整的前缀和名字:

Java
1 2 3 < EditText      style = "@style/CodeFont.Red.Big"      . . . / >

Android对于第一种定义方式并没用限制,所以所有以第二种方式定义的Style都可以转用第一种:

Java
1 2 3

只要parent中的名字对应上实际定义的Style名字即可。不过换成第一种后Style的名字如果太简洁就容易冲突了。

两种继承方式混合的效果

前面说到Style的两种继承方式的效果是一致的,那假如将两种方式混在一起定义一个Style又会是什么样的效果呢?下边就用实际例子来分析一下。

首先定义一些实验所需的自定义属性(attr),(这样可以减少系统属性的干扰,因为系统总是会为它的属性定义值,那样可能无法分辨最后的效果是来自系统还是定义的值)

Java <?xml version="1.0" encoding="utf-8"?>
1 2 3 4 5 6 7 8 <? xml version = "1.0" encoding = "utf-8" ?>      < declare - styleable name = "CustomStyle" >          < attr name = "customColor" format = "color" / >          < attr name = "customText" format = "string" / >          < attr name = "customSize" format = "dimension" / >      < / declare - styleable > < / resources >

接着定义一个TextView的子类,并在其中获取上边自定义属性的值并赋予TextView去呈现:

Java import android.util.TypedValue; import android.widget.TextView; /** * @author Ider */ public class StyledTextView extends TextView { public StyledTextView(Context context) { this(context, null); } public StyledTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public StyledTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); final TypedArray a = context.getTheme() .obtainStyledAttributes(attrs, R.styleable.CustomStyle, defStyleAttr, 0); final CharSequence text = a.getText(R.styleable.CustomStyle_customText); final int color = a.getColor(R.styleable.CustomStyle_customColor, Color.RED); final float size = a.getDimensionPixelSize(R.styleable.CustomStyle_customSize, 70); a.recycle(); setText(text); setTextColor(color); setTextSize(TypedValue.COMPLEX_UNIT_PX, size); } }
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 import android . util . TypedValue ; import android . widget . TextView ; /** * @author Ider */ public class StyledTextView extends TextView {      public StyledTextView ( Context context ) {          this ( context , null ) ;      }      public StyledTextView ( Context context , AttributeSet attrs ) {          this ( context , attrs , 0 ) ;      }      public StyledTextView ( Context context , AttributeSet attrs , int defStyleAttr ) {          super ( context , attrs , defStyleAttr ) ;          final TypedArray a = context . getTheme ( )                  . obtainStyledAttributes ( attrs , R . styleable . CustomStyle , defStyleAttr , 0 ) ;          final CharSequence text = a . getText ( R . styleable . CustomStyle_customText ) ;          final int color = a . getColor ( R . styleable . CustomStyle_customColor , Color . RED ) ;          final float size = a . getDimensionPixelSize ( R . styleable . CustomStyle_customSize , 70 ) ;          a . recycle ( ) ;          setText ( text ) ;          setTextColor ( color ) ;          setTextSize ( TypedValue . COMPLEX_UNIT_PX , size ) ;      } }

然后就是定义研究所需的Style

Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22                          < / resources >

上边定义的Style里,SuperStyleOne将通过添加前缀的方式作用到子Style上,而SuperStyleTwo则通过指定到parent来其作用。可以看到SubTwoSubThree混合了两种方式。

最后在Activity的布局视图里使用自定类并设定上不同的Style

XHTML
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 xmlns : android = "http://schemas.android.com/apk/res/android"               xmlns : tools = "http://schemas.android.com/tools"               android : orientation = "vertical"               android : layout_width = "match_parent"               android : layout_height = "match_parent"               android : paddingLeft = "@dimen/activity_horizontal_margin"               android : paddingRight = "@dimen/activity_horizontal_margin"               android : paddingTop = "@dimen/activity_vertical_margin"               android : paddingBottom = "@dimen/activity_vertical_margin"               tools : context = ".MainActivity" >                   style = "@style/SuperStyleOne"             android : layout_width = "wrap_content"             android : layout_height = "wrap_content" />                   style = "@style/SuperStyleOne.SubOne"             android : layout_width = "wrap_content"             android : layout_height = "wrap_content" />                   style = "@style/SuperStyleOne.SubTwo"             android : layout_width = "wrap_content"             android : layout_height = "wrap_content" />                   style = "@style/SuperStyleOne.SubThree"             android : layout_width = "wrap_content"             android : layout_height = "wrap_content" />

运行之后得到效果如下:

android 中style的使用_第1张图片

第一个和第二个都是Style标准的使用方式,也看到它们正确地获得了定义的属性值,子Style也正确的继承和覆盖了父Style的属性值。

对于第三个和第四个,它们呈现的颜色是代码中使用的默认红色(Color.RED),字体的值也是源自代码中的使用值,所以明显比前两者要小。这也就是说它们并没用继承下SuperStyleOne中定义的字体大小和颜色。但是SuperStyleTwo中定义的内容被第三个正确的显示了出来,也说明SubTwo成功继承通过parent指定的父Style的内容。而第四个呈现出来内容则说明覆盖的效果也是正确的。

在做这个试验之前,我一直以为两种方式会同时其作用,只是用parent指定比用前缀有高优先级。也就是说Android会先从当前Style定义中找某个属性的值,如果没有找到就转到parent指定的父Style中找,还没有则转到前缀指定的父Style中找。但是通过上边的结果表明:当使用parent指定父Style后,前缀方式则不在其作用,只是作为Style的名字。也就是说:Android的Style不支持多继承。Style的继承只能单线一层层下来。

反过来在看看系统定义的Style也更容易懂了,比如打开themes_holo.xml,会看到很多一样的内容被”冗余”地定义在Theme.HoloTheme.Holo.Light两个Style下。但因为Theme.Holo.Light用parent指定了其父Style是Theme.Light,所以Theme.Holo.Light并没有从Theme.Holo继承任何属性值,也因此这样的冗余是必须的。

Java
1 2 3 4 5

使用Theme.Holo.Light作为Style的名字只是为了名字更加的清晰明了。

References:

  1. Styles and Themes | Android Developers
  2. Android XML theme inheriting from two parent themes? – Stack Overflow
  3. xml – Reason why style attribute does not use the android: namespace prefix – Stack Overflow

更多相关文章

  1. 一起Talk Android吧(第一百四十八回:Android自定义View之Layout五)
  2. Android 自定义Button按钮显示样式(正常、按下、获取焦点)
  3. Android中实现跨进程通信(IPC)的方式(三)之观察者模式
  4. Android快捷方式
  5. Android--自定义视图控件(一)(Android Studio)
  6. Android自定义控件的使用
  7. Android中常用的有四种保存方式
  8. 一个非常好用的自定义圆形控件
  9. Android中如何自己定义吐司(Toast)

随机推荐

  1. android虚拟机大屏幕设置
  2. android基础之RelativeLayout布局
  3. Android(安卓)4编程入门经典—开发智能手
  4. Mac 安装 Android(安卓)Studio
  5. Android(安卓)Log日志保存到本地并读取
  6. 【Android】数据存储之Shared Preference
  7. Android(安卓)修改状态栏和标题栏颜色
  8. android背景选择器selector用法汇总
  9. 关于自定义布局,xml中配置属性(attrs)
  10. Android(安卓)图形学原理之OpenGL ES