Android 主题风格(Theme&Style)介绍

来源:互联网 发布:淘宝hd的微淘达人 编辑:程序博客网 时间:2024/06/07 09:40

使用 Style

Android 中的View、Window等控件通常会定义一些属性来表示各自的外观、格式等信息,例如一个TextView中的字体大小、字体颜色,一个Dialog的窗口类型、窗口大小等。而Style就是设置到一个View或者Window上的一系列属性的集合。我们可以将TextView的字体样式、字体颜色、字体大小等属性定义成一个Style,所有使用这个Style的TextView将会有相同的字体样式、字体颜色、字体大小。

Style一般是以xml文件的形式保存在资源目录中的,并且是与布局资源layout文件分离开来的,这样我们就可以将一套Style与使用这套Style的控件分离开来,进行独立维护,而且还可以将同一套Style复用到多个控件上,方便移植。从这一点上看Style有点类似于网页设计中的CSS,允许开发者将设计与内容分离。

下面是layout中一个TextView的定义,其中的typefacetextColortextSize几个属性是直接定义的:

<TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:typeface="monospace"    android:textColor="#ffff00ff"    android:textSize="24sp"    android:text="@string/hello_world"/>

如果我们将这几个属性分离出来,定义成一个Style,就会是下面的样子:
Style:

<style name="CodeFont">    <item name="android:layout_width">wrap_content</item>    <item name="android:layout_height">wrap_content</item>    <item name="android:typeface">monospace</item>    <item name="android:textColor">#ffff00ff</item>    <item name="android:textSize">24sp</item></style>

Layout:

<TextView    style="@style/CodeFont"    android:text="@string/hello_world" />

这样CodeFont这个Style还可以应用到其他的TextView,而且只需要修改CodeFont,所有使用这个Style的TextView都会改变样式。

使用 Theme

Theme其实就是应用到Activity或者Application上的Style。如上面我们刚刚定义的CodeFont,还可以按下面的方式应用到Activity或者Application上。在AndroidManifest.xml中可以如下设置:

<application    android:allowBackup="true"    android:icon="@drawable/ic_launcher"    android:label="@string/app_name"    android:theme="@style/CodeFont" >    <activity        android:name=".MainActivity"        android:label="@string/app_name"        android:theme="@style/CodeFont" >        ...    <activity>    ...</application>

如果Theme应用到Activity上,那么这个Activity中所有的View、Window都会使用Theme中定义的属性。同样,如果Theme应用到Application上,那么这个Application中所有的Activity中的View、Window都会使用Theme中定义的属性。

经过上面的介绍我们可以知道,Style和Theme的区别就在于应用的范围不同。Style是应用到单独的一个View上的,而Theme是应用到一个Activity或者Application中的View上的。

Style、Theme的继承性

Style是可以继承的,子Style可以继承父Style定义的属性,并重写父Style的属性。

如下面代码,Style CodeFont通过parent属性继承自系统的Style TextAppearance,并修改了textColortypeface等几个属性,而其他没有修改的属性默认会使用TextAppearance中的定义。

<style name="CodeFont" parent="@android:style/TextAppearance">    <item name="android:layout_width">fill_parent</item>    <item name="android:layout_height">wrap_content</item>    <item name="android:textColor">#00FF00</item>    <item name="android:typeface">monospace</item></style>

而如果是想从自己定义的Style继承,可以不使用parent关键字,而是把父Style名称做为子Style名称的前缀。如下面,CodeFont.Red继承自CodeFont,并修改了textColor属性为#FF0000

<style name="CodeFont.Red">    <item name="android:textColor">#FF0000</item></style>

使用系统Theme

Android系统资源中已经定义了很多的Style和Theme,如Android 4.0之前使用的默认Theme,Android 4.0之后引入的Holo系列Theme,Android 5.0之后引入的Material系列Theme。我们可以在自己的应用中直接使用这些系统内置的Theme或者从这些Theme继承子Theme并进行修改。

上面介绍的Theme并不是支持Android所有的版本,如Holo风格的Theme只支持Android 4.0+的版本,Material风格的Theme只支持Android 5.0+的版本,那么我们想让我们的应用能够自己根据不同的Android版本适配不同的Theme呢?看下面:
AndroidManifest.xml

<application    android:allowBackup="true"    android:icon="@drawable/ic_launcher"    android:label="@string/app_name"    android:theme="@style/AppTheme" >    ...</application>

res/values/styles.xml

<style name="AppTheme" parent="@android:style/Theme"></style>

res/values-v14/styles.xml

<style name="AppTheme" parent="@android:style/Theme.Holo"></style>

res/values-v21/styles.xml

<style name="AppTheme" parent="@android:style/Theme.Material"></style>

我们先在默认的资源目录res/values/下定义了一个AppTheme,继承自系统的默认Theme,在Android 4.0的资源目录res/values-v14/下面重新定义AppTheme,并继承自Theme.Holo,在Android 5.0的资源目录res/values-v21/下面重新定义AppTheme,并继承自Theme.Material,这样应用在Android 4.0之前的版本上运行时会使用默认Theme,在Android 4.0 ~Android 4.4版本上运行时会使用Holo主题,在Android 5.0+的版本上会使用Material主题。

定义 Style

前面了解了Styles都是在xml资源文件里定义的,那么在资源文件里定义的这些Styles最后是怎么应用到对应的View上的,而一个View的各个属性又是在哪里定义的呢?
我们先看一看Android系统资源的源码,没有源码的同学可以到http://androidxref.com/去看。
Android系统中定义的资源都是放在源码的frameworks/base/core/res目录里的,每个View属性都是定义在res/values/attrs.xml文件里的,如下面TextView属性的定义:
frameworks/base/core/res/res/values/attrs.xml

<declare-styleable name="TextView">    ...    <!-- Text to display. -->    <attr name="text" format="string" localization="suggested" />    <!-- Hint text to display when the text is empty. -->    <attr name="hint" format="string" />    <!-- Text color. -->    <attr name="textColor" />    ...</declare-styleable>

这里为TextView定义了一个<declare-styleable>元素,也就是定义了一个Style。里面的每个<attr>TextView定义了一个属性,如texthinttextColor等属性,format表示该属性的格式,主要有以下几种:

格式属性 说明 reference 指向其他资源的引用 string 字符串资源 integer 整数类型的资源 boolean 布尔值类型的资源 color 颜色资源 float 浮点型资源 dimension 带有单位的数据资源,如14sp15dp fraction 百分数资源,如20%30%p enum 枚举型资源 flags 位标记资源

解析Style

应用的编译过程中资源编译工具aapt会自动创建一个R.java文件,里面会为应用中所有的资源、属性定义一个ID。编译系统资源framework-res时也会生成一个R.java,里面会创建一个styleable类来存放所有View的属性定义,如下面是上面看到的TextView里定义的几个属性:
out/target/common/obj/APPS/framework-res_intermediates/src/android/R.java

public static final class styleable {    ...    public static final int TextView_text = 18;    public static final int TextView_hint = 19;    public static final int TextView_textColor = 5;    ...}

在资源类R.javatexthinttextColor几个属性被定义成了TextView_textTextView_hintTextView_textColor等的int型变量。那么这些变量是怎么使用的呢?
我们可以看一下TextView构造方法里的代码:
frameworks/base/core/java/android/widget/TextView.java

CharSequence text = "";CharSequence hint = null;final Resources.Theme theme = context.getTheme();TypedArray a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);int n = a.getIndexCount();for (int i = 0; i < n; i++) {    int attr = a.getIndex(i);    switch (attr) {    ...    case com.android.internal.R.styleable.TextView_hint:        hint = a.getText(attr);        break;    case com.android.internal.R.styleable.TextView_text:        text = a.getText(attr);        break;    ...    }}a.recycle();setText(text, bufferType);if (hint != null) setHint(hint);
  • 这里先定义了text、hint两个字符串;
  • 通过context.getTheme()得到一个Resources.Theme,这就是一个主题。;
  • 通过theme.obtainStyledAttributes得到一个TypedArray,这是一个资源属性数组,包含这我们在Theme里定义的所有属性的值;
  • 通过遍历TypedArray里的所有属性,找到TextView_hint、TextView_text属性对应的值,赋值给hint和text;
  • TextView使用text、hint的值。

上面介绍的是Style和Theme的定义方法和使用原理,至于更深层次的资源解析方面的问题暂时不做讨论。

下面有一个主题切换的小Demo,有兴趣的可以看一下。
https://github.com/geyunfei/ThemeDemo

Demo效果

原创粉丝点击