自定义 View 之 style,theme

Android 为我们提供了一系列的控件,TextView、Button、ImageView 等等,但是讲真,这些组件呆板单调,很多情况下,需要我们对其进行美化。

我们可以使用 style、theme 以及一系列 Drawable 来美化我们的窗口和组件。

样式 - style

style 可以帮助我们设置 View 的高度、填充、字体颜色、字体大小、背景色等等许多属性。style 在 XML 文件中设置,和设置布局的 XML 是分开的。

例如你可以把下面这个布局 XML:

<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textColor="#00FF00"
    android:typeface="monospace"
    android:text="@string/hello" />

变成下面这样:

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

所有和 style 相关的属性都从布局文件中移动到了一个名为 CodeFont 的 style 定义中:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="CodeFont" parent="@android:style/TextAppearance.Medium">
        <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>
</resources>

style 的定义和使用大致如此,但是其可以实现的功能却丰富的多。

style 的定义

每个 style 都是一个 XML 文件,保存在 /res/values/ 当中,可任意指定该 XML 文件的名称,但它必须使用 .xml 扩展名,并且必须保存在 res/values/ 文件夹内。

style 文件必须以 <resources> 为根节点。

每创建一个 style,需要向根节点中添加一个 <style> 元素,该元素必须有唯一的 name 属性作为唯一标识以供组件引用。每个 style 的属性都必须添加在一个 <item> 元素当中,该元素带有声明样式属性以及属性值的 name(该属性为必需属性)根据样式属性,<item> 的值可以是关键字字符串、十六进制颜色值、对另一资源类型的引用或其他值。以下是一个包含单个样式的示例文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="CodeFont" parent="@android:style/TextAppearance.Medium">
        <item name="android:layout_width">fill_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textColor">#FF0000</item>
        <item name="android:typeface">monospace</item>
    </style>
</resources>

样式的继承

我们可以通过 <style> 元素中的 parent 属性为该元素设置父元素,它可以继承或复写父元素的所有 item。

style 可以多重继承,用 . 来进行多重继承,例如:

    <style name="CodeFont.Red.Big">
        <item name="android:textSize">30sp</item>
    </style>

这个样式就是分别继承了 CondeFontCondeFont.Red,然后又添加了 textSize 属性(但是似乎是脱裤子放屁,但官方文档就是这么写的)。

样式属性

我们都可以在 style 中设置哪些属性呢?答案是:我们的 View 支持哪些属性,我们就可以设置哪些属性(这不是废话么...),我们可以通过查看控件代码来查找适用的属性,所有的可用 style,可以查看 R.attr 参考资料。

不过,某些样式属性任何 View 元素都不提供支持,只能以主题形式应用。 这些样式属性应用于整个窗口而非任何类型的 View。例如,主题的样式属性可以隐藏应用标题、隐藏状态栏或更改窗口的背景。 这些类型的样式属性不属于任何 View 对象。要发现这些仅主题样式属性,请在 R.attr 参考资料中查看有关以 window 开头的属性的内容。 例如,windowNoTitle 和 windowBackground 是只有在样式以主题形式应用于 Activity 或应用时才起作用的样式属性。

注:别忘了使用 android: 命名空间为每个 <item> 元素中的属性名称添加前缀。 例如:<item name="android:inputType">

样式的应用

应用样式主要有两种方法:

  • 如果是对单个视图应用样式,请为布局 XML 中的 View 元素添加 style 属性。
  • 或者,如果是对整个 Activity 或应用来应用样式,请为 Android 清单中的 <activity><application> 元素添加 android:theme 属性。

需要注意的是,当我们给一个 View 来设置 style 的时候,该 View 会应用这个 style,当我们给一个 ViewGroup 来设置 style 的时候,效果只产生于该 ViewGroup 本身,其子 View 并不会产生影响,不过我们可以通过设置 theme 来将样式作用于所有 View。

我们设置一个 style 给一个 View 或者 ViewGroup,目标只会对本身属性支持的 style 内容进行应用,不支持的属性将会忽略。

举个例子:

    <style name="CodeFont">
        <item name="android:typeface">monospace</item>
        <item name="android:textSize">20dp</item>
    </style>

这个 style 文件只是修改文本字体,当我们在清单文件中 <activity> 元素中通过 android:theme 来设置全局主题时,那么该 Activity 下所有字体都将变为 monospace 和 20dp,而不支持 typeface 和 textSize 属性的组件将自动忽略该 style。

但是如果将该 style 设置给布局文件,那么布局文件下的 TextView 却并不会受到影响。

假如我们为一个 View 指定了某项属性,但是在 XML 中又重新指定了该属性,那么该属性将以 XML 中重新设定的为准。

主题 - theme

除了对 View 做一些美化工作之外,我们还可以对我们的 Activity 进行一些特殊化设置,有两种方法:

  • <application> 当中添加 android:theme 属性,应用所有 Activity 全部生效
  • <activity> 当中添加 android:theme 属性,只有当前 Activity 生效

正如 Android 提供了其他内建资源一样,有许多预定义主题可供您使用,可免于自行编写。 例如,您可以使用 Dialog 主题,为您的 Activity 赋予类似对话框的外观:

<activity android:theme="@android:style/Theme.Dialog">

或者,如果您希望背景是透明的,则可使用 Translucent 主题:

<activity android:theme="@android:style/Theme.Translucent">

如果您喜欢某个主题,但想做些调整,只需将该主题添加为您的自定义主题的 parent。 例如,您可以像下面这样对传统明亮主题进行修改,使用您自己的颜色:

<color name="custom_theme_color">#b0b0ff</color>
<style name="CustomTheme" parent="android:Theme.Light">
    <item name="android:windowBackground">@color/custom_theme_color</item>
    <item name="android:colorBackground">@color/custom_theme_color</item>
</style>

(请注意,此处颜色需要以单独资源形式提供,因为 android:windowBackground 属性仅支持对另一资源的引用;不同于 android:colorBackground,无法为其提供颜色字面量。)

现在,在 Android 清单内使用 CustomTheme 替代 Theme.Light:

<activity android:theme="@style/CustomTheme">

根据平台版本选择主题

新版本的 Android 可为应用提供更多主题,您可能希望在这些平台上运行时可以使用这些新增主题,同时仍可兼容旧版本。 您可以通过自定义主题来实现这一目的,该主题根据平台版本利用资源选择在不同父主题之间切换。

例如,以下这个声明所对应的自定义主题就是标准的平台默认明亮主题。 它位于 res/values 之下的一个 XML 文件(通常是 res/values/styles.xml)中:

<style name="LightThemeSelector" parent="android:Theme.Light">
    ...
</style>

为了让该主题在应用运行在 Android 3.0(API 级别 11)或更高版本系统上时使用更新的全息主题,您可以在 res/values-v11 下的 XML 文件中加入一个替代主题声明,但将父主题设置为全息主题:

<style name="LightThemeSelector" parent="android:Theme.Holo.Light">
    ...
</style>

现在像您使用任何其他主题那样使用该主题,您的应用将在其运行于 Android 3.0 或更高版本的系统上时自动切换到全息主题。

R.styleable.Theme 提供了可在主题中使用的标准属性的列表。

如需查看更详实的 Android 样式和主题参考资料,请参阅以下源代码:

Resource

Android 中的资源体系庞大而复杂,因为篇幅问题,可以看官方的链接,已经讲解的很清楚了:

资源概览