Style在Android中的继承关系

本文深入探讨Android中的样式(Style)和主题(Theme)概念,特别是样式继承机制。文章通过实例展示了如何定义和使用样式,包括两种继承方式及其混合效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;

Source code         
  1. <style name = "GreenText" parent = "@android:style/TextAppearance" >
  2. <item name = "android:textColor" > #00FF00 </item >
  3. </style >

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

Source code         
  1. <style name = "CodeFont.Red" >
  2. <item name = "android:textColor" > #FF0000 </item >
  3. </style >

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

Source code         
  1. <style name = "CodeFont.Red.Big" >
  2. <item name = "android:textSize" > 30sp </item >
  3. </style >

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

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

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

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

Source code         
  1. <style name = "Big" parent = "CodeFont.Red" >
  2. <item name = "android:textSize" > 30sp </item >
  3. </style >

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

两种继承方式混合的效果

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

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

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

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

Source code         
  1. package com.ider.trial.styles ;
  2.  
  3. import android.content.Context ;
  4. import android.content.res.TypedArray ;
  5. import android.graphics.Color ;
  6. import android.util.AttributeSet ;
  7. import android.util.TypedValue ;
  8. import android.widget.TextView ;
  9.  
  10. /**
  11.  * @author Ider
  12.  */
  13. public class StyledTextView extends TextView {
  14.  
  15. public StyledTextView (  Context context ) {
  16. this ( context, null ) ;
  17. }
  18.  
  19. public StyledTextView (  Context context,  AttributeSet attrs ) {
  20. this ( context, attrs, 0 ) ;
  21. }
  22.  
  23. public StyledTextView (  Context context,  AttributeSet attrs, int defStyleAttr ) {
  24. super ( context, attrs, defStyleAttr ) ;
  25.  
  26. final TypedArray a = context. getTheme ( )
  27. . obtainStyledAttributes ( attrs, R. styleable . CustomStyle , defStyleAttr, 0 ) ;
  28. final CharSequence text = a. getText ( R. styleable . CustomStyle_customText ) ;
  29. final int color = a. getColor ( R. styleable . CustomStyle_customColor ,  Color . RED ) ;
  30. final float size = a. getDimensionPixelSize ( R. styleable . CustomStyle_customSize , 70 ) ;
  31. a. recycle ( ) ;
  32.  
  33. setText ( text ) ;
  34. setTextColor ( color ) ;
  35. setTextSize ( TypedValue. COMPLEX_UNIT_PX , size ) ;
  36.  
  37. }
  38. }

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

Source code         
  1. <resources >
  2. <style name = "SuperStyleOne" >
  3. <item name = "customColor" > @android:color/holo_orange_dark </item >
  4. <item name = "customText" > Hello World </item >
  5. <item name = "customSize" > 30dp </item >
  6. </style >
  7.  
  8. <style name = "SuperStyleTwo" >
  9. <item name = "customText" > www.iderzheng.com </item >
  10. </style >
  11.  
  12. <style name = "SuperStyleOne.SubOne" >
  13. <item name = "customColor" > @android:color/holo_blue_dark </item >
  14. </style >
  15.  
  16. <style name = "SuperStyleOne.SubTwo" parent = "SuperStyleTwo" >
  17. </style >
  18.  
  19. <style name = "SuperStyleOne.SubThree" parent = "SuperStyleTwo" >
  20. <item name = "customText" > blog.iderzheng.com </item >
  21. </style >
  22. </resources >

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

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

Source code         
  1. <LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
  2. xmlns:tools = "http://schemas.android.com/tools"
  3. android:orientation = "vertical"
  4. android:layout_width = "match_parent"
  5. android:layout_height = "match_parent"
  6. android:paddingLeft = "@dimen/activity_horizontal_margin"
  7. android:paddingRight = "@dimen/activity_horizontal_margin"
  8. android:paddingTop = "@dimen/activity_vertical_margin"
  9. android:paddingBottom = "@dimen/activity_vertical_margin"
  10. tools:context = ".MainActivity" >
  11.  
  12. <com.ider.trial.styles.StyledTextView
  13. style = "@style/SuperStyleOne"
  14. android:layout_width = "wrap_content"
  15. android:layout_height = "wrap_content" />
  16.  
  17. <com.ider.trial.styles.StyledTextView
  18. style = "@style/SuperStyleOne.SubOne"
  19. android:layout_width = "wrap_content"
  20. android:layout_height = "wrap_content" />
  21.  
  22. <com.ider.trial.styles.StyledTextView
  23. style = "@style/SuperStyleOne.SubTwo"
  24. android:layout_width = "wrap_content"
  25. android:layout_height = "wrap_content" />
  26.  
  27. <com.ider.trial.styles.StyledTextView
  28. style = "@style/SuperStyleOne.SubThree"
  29. android:layout_width = "wrap_content"
  30. android:layout_height = "wrap_content" />
  31.  
  32. </LinearLayout >

运行之后得到效果如下:

第一个和第二个都是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.Holo 和 Theme.Holo.Light 两个Style下。但因为 Theme.Holo.Light 用parent指定了其父Style是 Theme.Light ,所以 Theme.Holo.Light 并没有从Theme.Holo继承任何属性值,也因此这样的冗余是必须的。

Source code         
  1. <style name = "Theme.Holo.Light" parent = "Theme.Light" >
  2.  
  3. ... ... ... ...
  4.  
  5. </style >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值