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;
1 2 3 |
<style name="GreenText" parent="@android:style/TextAppearance"> <item name="android:textColor">#00FF00</item> </style> |
另一种则是将父Style的名字作为前缀,然后通过“.”连接新定义Style的名字:
1 2 3 |
<style name="CodeFont.Red"> <item name="android:textColor">#FF0000</item> </style> |
第二种方式可以无限连接子Style来实践多层继承:
1 2 3 |
<style name="CodeFont.Red.Big"> <item name="android:textSize">30sp</item> </style> |
相对第一种,Android对第二种方式做出的限制就是Style必须是由自己定义的,或者说父Style和子Style必须是定义在同一个程序内,不能是引用第三方或系统的Style。毕竟对于系统的Style的引用是需要加上android:
前缀作为命名空间。
其次在使用Style时,对于第二种方式定义的Style,必须引用其完全的名字,也就是说必须要包含完整的前缀和名字:
1 2 3 |
<EditText style="@style/CodeFont.Red.Big" ... /> |
Android对于第一种定义方式并没用限制,所以所有以第二种方式定义的Style都可以转用第一种:
1 2 3 |
<style name="Big" parent="CodeFont.Red"> <item name="android:textSize">30sp</item> </style> |
只要parent
中的名字对应上实际定义的Style名字即可。不过换成第一种后Style的名字如果太简洁就容易冲突了。
两种继承方式混合的效果
前面说到Style的两种继承方式的效果是一致的,那假如将两种方式混在一起定义一个Style又会是什么样的效果呢?下边就用实际例子来分析一下。
首先定义一些实验所需的自定义属性(attr),(这样可以减少系统属性的干扰,因为系统总是会为它的属性定义值,那样可能无法分辨最后的效果是来自系统还是定义的值)
1 2 3 4 5 6 7 8 |
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomStyle"> <attr name="customColor" format="color"/> <attr name="customText" format="string"/> <attr name="customSize" format="dimension"/> </declare-styleable> </resources> |
接着定义一个TextView的子类,并在其中获取上边自定义属性的值并赋予TextView去呈现:
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<resources> <style name="SuperStyleOne"> <item name="customColor">@android:color/holo_orange_dark</item> <item name="customText">Hello World</item> <item name="customSize">30dp</item> </style> <style name="SuperStyleTwo"> <item name="customText">www.iderzheng.com</item> </style> <style name="SuperStyleOne.SubOne"> <item name="customColor">@android:color/holo_blue_dark</item> </style> <style name="SuperStyleOne.SubTwo" parent="SuperStyleTwo"> </style> <style name="SuperStyleOne.SubThree" parent="SuperStyleTwo"> <item name="customText">blog.iderzheng.com</item> </style> </resources> |
上边定义的Style里,SuperStyleOne
将通过添加前缀的方式作用到子Style上,而SuperStyleTwo
则通过指定到parent
来其作用。可以看到SubTwo
和SubThree
混合了两种方式。
最后在Activity的布局视图里使用自定类并设定上不同的Style
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 |
<LinearLayout 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"> <com.ider.trial.styles.StyledTextView style="@style/SuperStyleOne" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <com.ider.trial.styles.StyledTextView style="@style/SuperStyleOne.SubOne" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <com.ider.trial.styles.StyledTextView style="@style/SuperStyleOne.SubTwo" android:layout_width="wrap_content" android:layout_height="wrap_content"/> |