Android经典应用程序开发
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.6 样式和主题的使用

Android中,样式(Style)是一种用于描述一组属性的机制,可以应用于控件(View)或者窗口。

样式通常有两种使用方法:

(1)在布局文件中将其应用于控件标签,此时通常依然称之为样式(style)。

(2)在AndroidManifest.xml中将其应用于一个Activity或者一个application,此时通常称之为主题(Theme)。此时相当于将这个样式应用于其中的所有控件。

样式可以使用系统的预置的定义,也可以自定义。预定义使用R.style类,自定义样式通常在一个XML文件中使用<style>标签来描述。

2.6.1 控件中的样式

控件的样式让布局文件中的众多控件拥有相同的属性,使用样式不仅可以减少布局文件中重复的内容,也有利于全局控制控件的外观等公共属性。

Android中预定义的样式,使用R.style类中的各个成员来表示。样式的本质是对各个XML属性进行定义,这些XML属性在Android中使用R.attr类来表示。

每个控件的style属性可以用于设置它的样式,在布局文件中可以使用类似style="@android:style/MediaButton"的方式。

例如,在某个布局文件中一个控件的定义如下所示:

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

此时如果认为除了android:text之外的属性都是作为公共的内容实现的,可以利用样式将这个控件写成如下方式:

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

此时的style属性被设置为名称为CodeFont的样式。可以使用res/values/目录中的styles.xml文件进行定义,内容如下所示。

        <?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>

这里进行的自定义样式CodeFont继承了Android预置的样式TextAppearance.Medium,并且定义了android:textColor等几个属性。定义完成后可以在各个布局文件中使用。

2.6.2 全局性质的主题

在AndroidManifest.xml中使用android:theme属性来表示设置一种样式作为全局性的内容。<application>和<activity>标签具有这个属性。此时应用的样式中的内容,对其中的所有内容都起作用。

Android预置的主题来自android.R.style中的Theme_*等常量。除了预置的主题,可以继承预定义样式实现自定义主题,也可以生成完全的自定义主题。

提示:主题和样式在本质上是相同的,都是对XML文件中的一组属性进行默认的定义。在Android中,应用于控件时称之为样式(Style),应用于活动和应用时称之为主题(Theme),后者通常比前者多了一些和窗口属性相关的定义。

主题的设置也可以在代码中使用,这种方式并不常用。Activity的父类ContextThemeWrapper中具有如下的方法:

        public void setTheme (int resid)

1.预定义主题

android.R.style类中有属性常量名称为Theme_Light_NoTitleBar_Fullscreen,那么在AndroidManifest.xml使用的就是如下的格式:

需要将“_”替换成“.”,然后可以在AndroidManifest.xml中使用。大多数预定义样式都有子类,Theme.<style>.NoTitleBar表示使用该样式加无标题栏的效果,Theme.<style>.NoTitleBar.Fullscreen表示使用该样式加全屏的效果。

使用不同样式示例程序:Controls(ApiDemo=>Views=>Controls)

源代码:

              com/example/android/apis/view/Controls1.java
              com/example/android/apis/view/Controls2.java

布局文件:controls_1.xml

使用不同样式的示例程序如图2-19所示。

图2-19 使用不同样式的示例程序(左:Theme.Light样式;右:默认样式)

两个活动使用同样的布局文件,两个活动的内容基本是相同的,运行的效果却是一个白底色一个黑底色。它们的主要区别体现在AndroidManifest.xml中,如下所示:

        <activity android:name=".view.Controls1"
              android:label="Views/Controls/1. Light Theme"
              android:theme="@android:style/Theme.Light">
            <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.SAMPLE_CODE" />
            </intent-filter>
        </activity>
        <activity android:name=".view.Controls2"
                  android:label="Views/Controls/2. Default Theme">
            <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.SAMPLE_CODE" />
            </intent-filter>
        </activity>

第二个程序使用了默认样式,而第一个程序使用了"@android:style/Theme.Light",表示使用的是Theme.Light样式,在这种情况下底色变成了浅色的效果。

在Activity的代码中进行的样式设置如下所示:

        protected void onCreate(Bundle savedInstanceState) {
            setTheme(android.R.style.Theme_NoTitleBar);
            super.onCreate(savedInstanceState);
            // ......省略活动的创建过程
        }

对于代码中的样式设置,由于执行场合不同,因此即使将设置的代码放在Activityd的onCreate()方法的最前面,也不能保证和在AndroidManifest.xml中设置的效果相同。

2.自定义主题

根据预定义样式,可以通过继承的方法实现自定义的样式。这种自定义样式通常只改变预定义样式的部分属性,一般可以在res/values/styles.xml文件中通过<style>标签来完成定义,然后将其设置到AndroidManifest.xml中的android:theme属性。此时使用的是类似android:theme="@style/MyTheme"的格式。

参考程序为一个对话框样式的活动和一个自定义对话框样式的活动。

示例程序:

                    DialogActivity(ApiDemo=>App=>Activity=> DialogActivity)
                    CustomDialogActivity(ApiDemo=>App=>Activity=>CustomDialogActivity)

源代码:com/example/android/apis/app/DialogActivity.java

布局文件:custom_dialog_activity.xml

样式文件:res/values/styles.xml

两个活动运行结果如图2-20所示。

图2-20 预定样式和自定义样式实现类似对话框的活动运行结果

上述两个程序本质上都是活动,但是显示的效果却类似于一个对话框。透过对话框透明的部分,可以看到下面的活动。

这两个程序的布局文件和源代码都并无特别的地方。效果是通过在AndroidManifest.xml中设置其样式android:theme来完成的。

AndroidManifest.xml中的定义如下所示:

        <activity android:name=".app.DialogActivity"
              android:label="@string/activity_dialog"
              android:theme="@android:style/Theme.Dialog" >
              <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.SAMPLE_CODE" />
              </intent-filter>
        </activity>
        <activity android:name=".app.CustomDialogActivity"
              android:label="@string/activity_custom_dialog"
              android:theme="@style/Theme.CustomDialog“ >
            <intent-filter>
                  <action android:name="android.intent.action.MAIN" />
                  <category android:name="android.intent.category.SAMPLE_CODE" />
            </intent-filter>
        </activity>

DialogActivity将样式设置为“@android:style/Theme.Dialog”,表示使用预定义的样式Theme.Dialog。

CustomDialogActivity将样式设置为“@style/Theme.CustomDialog”,表示使用名称为Theme.CustomDialog的自定义样式。

CustomDialog是一个自定义样式,在res/values/styles.xml中进行定义,如下所示:

        <style name="Theme.CustomDialog" parent="android:style/Theme.Dialog">
              <item name="android:windowBackground">@drawable/filled_box</item>
        </style>

使用“parent”表示由CustomDialog本身继承的预定义样式,重新定义了窗口的背景为drawable中的filled_box。这里引用了filled_box.xml文件,这个文件在res/drawable中,其中定义了相关内容。在定义的样式中,通过设置更多的值来获得不同的窗口效果。通过自定义样式文件可以获得复用效果。

提示:类似的对话框场景,前台的程序没有覆盖住整个屏幕,此时后面依然处于可见状态的活动是onPause()状态,通常情况下,活动是完全被遮挡的,处于onStop()状态。

3.完全自定义样式作为主题

样式可以在程序中全新定义,通过定制各个属性进行定义。这些属性来自于R.attr类中的内容进行定义。具体每一个属性可以使用的值,可以从R.attr类的帮助文档中找到。

由于继承主题的方式实际上使用的是“单继承”,因此如果想组合主题,随心所欲地设置效果,最终的方式还是直接设置属性。

例如,如果在res/values/styles.xml中定义如下内容:

        <style name="Theme.WindowTranslucent ">
            <item name="android:windowIsTranslucent">true</item>
        </style>

定义名称为Theme.WindowTranslucent的样式,表示背景透明的效果。这里进行自定义的windowIsTranslucent就是R.attr类的一个成员。虽然主题只能继承一个,但是可以进行多数量属性的设置。

在Android程序中,当某一个活动启动之后,可能需要使用背景透明的效果,本例用于描述背景透明的应用。

参考示例程序:

                  TranslucentActivity(ApiDemo=>App=>Activity=>Translucent)
                  TranslucentBlurActivity(App=>Activity=>TranslucentBlur)

源代码:

              com/example/android/apis/app/TranslucentActivity.java
              com/example/android/apis/app/TranslucentBlurActivity.java

样式文件:values/styles.xml

透明程序的运行结果如图2-21所示。

图2-21 TranslucentActivity和TranslucentBlurActivity程序的运行结果

AndroidManifest.xml中的定义如下所示:

        <activity android:name=".app.TranslucentActivity"
              android:label="@string/activity_translucent"
              android:theme="@style/Theme.Translucent“ >
            <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.SAMPLE_CODE" />
            </intent-filter>
        </activity>
        <activity android:name=".app.TranslucentBlurActivity"
              android:label="@string/activity_translucent_blur"
              android:theme="@style/Theme.Transparent“ >
            <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.SAMPLE_CODE" />
            </intent-filter>
        </activity>

两个活动分别使用Theme.Translucent和Theme.Transparent主题,它们都是自定义的主题,内容在values/styles.xml中。

Theme.Translucent主题如下所示:

        <style name="Theme.Translucent" parent="android:style/Theme.Translucent">
            <item name="android:windowBackground">
                  @drawable/translucent_background</item>
            <item name="android:windowNoTitle">true</item>
            <item name="android:colorForeground">#fff</item>
        </style>

Theme.Transparent主题如下所示:

        <style name="Theme.Transparent">
            <item name="android:windowIsTranslucent">true</item>
            <item name="android:windowAnimationStyle">
                        @android:style/Animation.Translucent</item>
            <item name="android:windowBackground">
                        @drawable/transparent_background</item>
            <item name="android:windowNoTitle">true</item>
            <item name="android:colorForeground">#fff</item>
        </style>

Theme.Transparent实际上是一个没有继承预定义主题的主题,在其中自定义了windowIsTranslucent和windowAnimationStyle属性,取得了和继承Theme.Translucent类似的效果,并且使用了windowNoTitle表示设置为没有标题栏。

两个主题使用的背景数值在colors.xml中定义,如下所示:

        <drawable name="translucent_background">#e0000000</drawable>
        <drawable name="transparent_background">#00000000</drawable>

除此之外,TranslucentBlurActivity之所以能够获得背景模糊的效果,是因为在源代码中进行了进一步的设置,如下所示:

        public class TranslucentBlurActivity extends Activity {
            protected void onCreate(Bundle icicle) {
              super.onCreate(icicle);
              getWindow().       // 获得窗口,用以设置其中的特性
                  setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
                                  WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
              setContentView(R.layout.translucent_background);
            }
        }

设置模糊效果是通过窗口管理器设置参数来完成的,这种设置只有在背景设置为透明后才能显示效果。