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); } }
设置模糊效果是通过窗口管理器设置参数来完成的,这种设置只有在背景设置为透明后才能显示效果。