原文:Android菜鸟的成长笔记(6)——剖析源码学自定义主题Theme
还记得在Android菜鸟的成长笔记(3)中我们曾经遇到了一个问题吗?"这个界面和真真的QQ界面还有点不同的就是上面的标题myFirstApp,怎么去掉这个标题呢?",当时我直接在AndroidMainfest.xml中添加了一个属性:
android:theme="@android:style/Theme.NoTitleBar"
可能有的朋友就会迷惑了,为什么添加了这个属性就可以了。这一篇文章将让我们一起翻开Android系统源代码来揭开困扰大家的关于主题使用以及自定义的谜团。
一、样式(Style)与主题(Theme)
在Android的应用的资源文件中有一个style.xml文件,这个文件是干什么用的呢?我们有时候经常需要对某个类型的组件指定大致相似的格式,比如字体、颜色、背景色等,如果我们每次都为某个View组件去重复指定这些属性,这无疑会产生大量的工作,而且还不利于后期的代码修改和维护。而Style就是一个样式的格式,这个格式可以被多个View组件所使用,也可以说是一个样式的集合类,被需要这一类样式集合的View组件所使用。例如我们前面写的QQ登录界面中的登录按钮,我们可以给定义一个样式
<style name="buttonStyle">
<item name="android:background">@drawable/login_button_nor</item>
<item name="android:textColor">@color/buttonTextColor</item>
</style>
在布局文件中引入样式
<Button
android:layout_width="270dip"
android:layout_height="40dip"
android:text="@string/login_button"
style="@style/buttonStyle"
/>
与样式非常相似,主题资源的xml文件通常也放在/res/values目录下,主题相当于整个应用或者某个Activity的样式,换句话说主题是针对窗体级别或整个应用程序的样式。与样式比较,样式是针对窗体内元素的样式。主题的设置有两种方式
(1)在AndroidMainfest.xml中为Activity或者application指定主题
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar" >
<activity
android:name="com.example.myfirstapp.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
上面AndroidMainfest.xml代码中给整个应用指定了一个主题,这个主题是没有标题栏的系统主题。
(2)在Activity创建时调用 setTheme方法(必须在setContentView前面调用)来给某个Activity添加主题。
二、剖析主题(Theme)资源
我们先来创建一个工程名字为helloworld,然后打开它的AndroiodMainfest.xml文件
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.helloworld.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
可以看到我们在创建工程时,已经默认给我们的整个应用添加了一个主题
android:theme="@style/AppTheme"
打开我们资源文件res/values/下面的styles.xml文件,可以看到在样式文件中有一个名字为AppTheme的样式,如下:
<resources> <!--
Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
-->
<style name="AppBaseTheme" parent="android:Theme.Light">
<!--
Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.
-->
</style> <!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style> </resources>
我们可以看到,这个AppTheme的样式继承自上面的AppBaseTheme。而AppBaseTheme又继承自系统的一个样式Theme.Light,打开Android系统源代码找到Theme.xml文件中的Theme.Light如下:
<!-- Theme for a light background with dark text on top. Set your activity
to this theme if you would like such an appearance. As with the
default theme, you should try to assume little more than that the
background will be a light color. -->
<style name="Theme.Light">
<item name="windowBackground">@drawable/screen_background_light</item>
<item name="colorBackground">@android:color/background_light</item>
<item name="colorForeground">@color/bright_foreground_light</item>
<item name="colorForegroundInverse">@android:color/bright_foreground_light_inverse</item> <item name="textColorPrimary">@android:color/primary_text_light</item>
<item name="textColorSecondary">@android:color/secondary_text_light</item>
<item name="textColorTertiary">@android:color/tertiary_text_light</item>
<item name="textColorPrimaryInverse">@android:color/primary_text_dark</item>
<item name="textColorSecondaryInverse">@android:color/secondary_text_dark</item>
<item name="textColorTertiaryInverse">@android:color/tertiary_text_dark</item>
<item name="textColorPrimaryDisableOnly">@android:color/primary_text_light_disable_only</item>
<item name="textColorPrimaryInverseDisableOnly">@android:color/primary_text_dark_disable_only</item>
<item name="textColorPrimaryNoDisable">@android:color/primary_text_light_nodisable</item>
<item name="textColorSecondaryNoDisable">@android:color/secondary_text_light_nodisable</item>
<item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_dark_nodisable</item>
<item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_dark_nodisable</item>
<item name="textColorHint">@android:color/hint_foreground_light</item>
<item name="textColorHintInverse">@android:color/hint_foreground_dark</item> <item name="popupWindowStyle">@android:style/Widget.PopupWindow</item> <item name="textCheckMark">@android:drawable/indicator_check_mark_light</item>
<item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_dark</item> <item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView.White</item>
<item name="expandableListViewStyle">@android:style/Widget.ExpandableListView.White</item>
<item name="listViewStyle">@android:style/Widget.ListView.White</item>
<item name="listDivider">@drawable/divider_horizontal_bright</item>
<item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item> <item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item>
<item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
<item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
<item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
<item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item>
</style>
样式的继承有两种方式,一种是上面看到的parent="",还有一种就是用”."的方式,上面的Theme.Light继承自Theme。这两种继承有什么区别呢?一个相当于咱们类之间的继承extend,另一个相当于内部类,也可以使用外部类的属性和方法。我们再来看看Theme.Light的父类Theme的样式定义。
<style name="Theme"> <item name="colorForeground">@android:color/bright_foreground_dark</item>
<item name="colorForegroundInverse">@android:color/bright_foreground_dark_inverse</item>
<item name="colorBackground">@android:color/background_dark</item>
<item name="colorBackgroundCacheHint">?android:attr/colorBackground</item>
<item name="disabledAlpha">0.5</item>
<item name="backgroundDimAmount">0.6</item> <!-- Text styles -->
<item name="textAppearance">@android:style/TextAppearance</item>
<item name="textAppearanceInverse">@android:style/TextAppearance.Inverse</item> <item name="textColorPrimary">@android:color/primary_text_dark</item>
<item name="textColorSecondary">@android:color/secondary_text_dark</item>
<item name="textColorTertiary">@android:color/tertiary_text_dark</item>
<item name="textColorPrimaryInverse">@android:color/primary_text_light</item>
<item name="textColorSecondaryInverse">@android:color/secondary_text_light</item>
<item name="textColorTertiaryInverse">@android:color/tertiary_text_light</item>
<item name="textColorPrimaryDisableOnly">@android:color/primary_text_dark_disable_only</item>
<item name="textColorPrimaryInverseDisableOnly">@android:color/primary_text_light_disable_only</item>
<item name="textColorPrimaryNoDisable">@android:color/primary_text_dark_nodisable</item>
<item name="textColorSecondaryNoDisable">@android:color/secondary_text_dark_nodisable</item>
<item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_light_nodisable</item>
<item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_light_nodisable</item>
<item name="textColorHint">@android:color/hint_foreground_dark</item>
<item name="textColorHintInverse">@android:color/hint_foreground_light</item>
<item name="textColorSearchUrl">@android:color/search_url_text</item> <item name="textAppearanceLarge">@android:style/TextAppearance.Large</item>
<item name="textAppearanceMedium">@android:style/TextAppearance.Medium</item>
<item name="textAppearanceSmall">@android:style/TextAppearance.Small</item>
<item name="textAppearanceLargeInverse">@android:style/TextAppearance.Large.Inverse</item>
<item name="textAppearanceMediumInverse">@android:style/TextAppearance.Medium.Inverse</item>
<item name="textAppearanceSmallInverse">@android:style/TextAppearance.Small.Inverse</item>
<item name="textAppearanceSearchResultTitle">@android:style/TextAppearance.SearchResult.Title</item>
<item name="textAppearanceSearchResultSubtitle">@android:style/TextAppearance.SearchResult.Subtitle</item> <item name="textAppearanceButton">@android:style/TextAppearance.Widget.Button</item> <item name="candidatesTextStyleSpans">@android:string/candidates_style</item> <item name="textCheckMark">@android:drawable/indicator_check_mark_dark</item>
<item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_light</item> <!-- Button styles -->
<item name="buttonStyle">@android:style/Widget.Button</item> <item name="buttonStyleSmall">@android:style/Widget.Button.Small</item>
<item name="buttonStyleInset">@android:style/Widget.Button.Inset</item> <item name="buttonStyleToggle">@android:style/Widget.Button.Toggle</item> <!-- List attributes -->
<item name="listPreferredItemHeight">64dip</item>
<!-- @hide -->
<item name="searchResultListItemHeight">58dip</item>
<item name="listDivider">@drawable/divider_horizontal_dark</item>
<item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator</item> <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
<item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item> <item name="expandableListPreferredItemPaddingLeft">40dip</item>
<item name="expandableListPreferredChildPaddingLeft">
?android:attr/expandableListPreferredItemPaddingLeft</item> <item name="expandableListPreferredItemIndicatorLeft">3dip</item>
<item name="expandableListPreferredItemIndicatorRight">33dip</item>
<item name="expandableListPreferredChildIndicatorLeft">
?android:attr/expandableListPreferredItemIndicatorLeft</item>
<item name="expandableListPreferredChildIndicatorRight">
?android:attr/expandableListPreferredItemIndicatorRight</item> <!-- Gallery attributes -->
<item name="galleryItemBackground">@android:drawable/gallery_item_background</item> <!-- Window attributes -->
<item name="windowBackground">@android:drawable/screen_background_dark</item>
<item name="windowFrame">@null</item>
<item name="windowNoTitle">false</item>
<item name="windowFullscreen">false</item>
<item name="windowIsFloating">false</item>
<item name="windowContentOverlay">@android:drawable/title_bar_shadow</item>
<item name="windowShowWallpaper">false</item>
<item name="windowTitleStyle">@android:style/WindowTitle</item>
<item name="windowTitleSize">25dip</item>
<item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Activity</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item> <!-- Dialog attributes -->
<item name="alertDialogStyle">@android:style/AlertDialog</item> <!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_background</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
<!-- These three attributes do not seems to be used by the framework. Declared public though -->
<item name="panelColorBackground">#000</item>
<item name="panelColorForeground">?android:attr/textColorPrimary</item>
<item name="panelTextAppearance">?android:attr/textAppearance</item> <!-- Scrollbar attributes -->
<item name="scrollbarFadeDuration">250</item>
<item name="scrollbarDefaultDelayBeforeFade">300</item>
<item name="scrollbarSize">10dip</item>
<item name="scrollbarThumbHorizontal">@android:drawable/scrollbar_handle_horizontal</item>
<item name="scrollbarThumbVertical">@android:drawable/scrollbar_handle_vertical</item>
<item name="scrollbarTrackHorizontal">@null</item>
<item name="scrollbarTrackVertical">@null</item> <!-- Text selection handle attributes -->
<item name="textSelectHandleLeft">@android:drawable/text_select_handle_left</item>
<item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item>
<item name="textSelectHandle">@android:drawable/text_select_handle_middle</item>
<item name="textSelectHandleWindowStyle">@android:style/Widget.TextSelectHandle</item> <!-- Widget styles -->
<item name="absListViewStyle">@android:style/Widget.AbsListView</item>
<item name="autoCompleteTextViewStyle">@android:style/Widget.AutoCompleteTextView</item>
<item name="checkboxStyle">@android:style/Widget.CompoundButton.CheckBox</item>
<item name="dropDownListViewStyle">@android:style/Widget.ListView.DropDown</item>
<item name="editTextStyle">@android:style/Widget.EditText</item>
<item name="expandableListViewStyle">@android:style/Widget.ExpandableListView</item>
<item name="expandableListViewWhiteStyle">@android:style/Widget.ExpandableListView.White</item>
<item name="galleryStyle">@android:style/Widget.Gallery</item>
<item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView</item>
<item name="gridViewStyle">@android:style/Widget.GridView</item>
<item name="imageButtonStyle">@android:style/Widget.ImageButton</item>
<item name="imageWellStyle">@android:style/Widget.ImageWell</item>
<item name="listViewStyle">@android:style/Widget.ListView</item>
<item name="listViewWhiteStyle">@android:style/Widget.ListView.White</item>
<item name="popupWindowStyle">@android:style/Widget.PopupWindow</item>
<item name="progressBarStyle">@android:style/Widget.ProgressBar</item>
<item name="progressBarStyleHorizontal">@android:style/Widget.ProgressBar.Horizontal</item>
<item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small</item>
<item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item>
<item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item>
<item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item>
<item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
<item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>
<item name="seekBarStyle">@android:style/Widget.SeekBar</item>
<item name="ratingBarStyle">@android:style/Widget.RatingBar</item>
<item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item>
<item name="ratingBarStyleSmall">@android:style/Widget.RatingBar.Small</item>
<item name="radioButtonStyle">@android:style/Widget.CompoundButton.RadioButton</item>
<item name="scrollViewStyle">@android:style/Widget.ScrollView</item>
<item name="horizontalScrollViewStyle">@android:style/Widget.HorizontalScrollView</item>
<item name="spinnerStyle">@android:style/Widget.Spinner</item>
<item name="starStyle">@android:style/Widget.CompoundButton.Star</item>
<item name="tabWidgetStyle">@android:style/Widget.TabWidget</item>
<item name="textViewStyle">@android:style/Widget.TextView</item>
<item name="webTextViewStyle">@android:style/Widget.WebTextView</item>
<item name="webViewStyle">@android:style/Widget.WebView</item>
<item name="dropDownItemStyle">@android:style/Widget.DropDownItem</item>
<item name="spinnerDropDownItemStyle">@android:style/Widget.DropDownItem.Spinner</item>
<item name="spinnerItemStyle">@android:style/Widget.TextView.SpinnerItem</item>
<item name="dropDownHintAppearance">@android:style/TextAppearance.Widget.DropDownHint</item>
<item name="keyboardViewStyle">@android:style/Widget.KeyboardView</item>
<item name="quickContactBadgeStyleWindowSmall">@android:style/Widget.QuickContactBadge.WindowSmall</item>
<item name="quickContactBadgeStyleWindowMedium">@android:style/Widget.QuickContactBadge.WindowMedium</item>
<item name="quickContactBadgeStyleWindowLarge">@android:style/Widget.QuickContactBadge.WindowLarge</item>
<item name="quickContactBadgeStyleSmallWindowSmall">@android:style/Widget.QuickContactBadgeSmall.WindowSmall</item>
<item name="quickContactBadgeStyleSmallWindowMedium">@android:style/Widget.QuickContactBadgeSmall.WindowMedium</item>
<item name="quickContactBadgeStyleSmallWindowLarge">@android:style/Widget.QuickContactBadgeSmall.WindowLarge</item> <!-- Preference styles -->
<item name="preferenceScreenStyle">@android:style/Preference.PreferenceScreen</item>
<item name="preferenceCategoryStyle">@android:style/Preference.Category</item>
<item name="preferenceStyle">@android:style/Preference</item>
<item name="preferenceInformationStyle">@android:style/Preference.Information</item>
<item name="checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item>
<item name="yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item>
<item name="dialogPreferenceStyle">@android:style/Preference.DialogPreference</item>
<item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item>
<item name="ringtonePreferenceStyle">@android:style/Preference.RingtonePreference</item>
<item name="preferenceLayoutChild">@android:layout/preference_child</item> <!-- Search widget styles -->
<item name="searchWidgetCorpusItemBackground">@android:color/search_widget_corpus_item_background</item>
</style>
我们可以看到里面定义了关于我们整个应用中文字的样式,按钮的样式,列表的样式,画廊的样式,窗体的样式,对话框的样式等。这个样式是系统的默认样式,也是最符合HOLO的样式。Theme中定义的是最基本的主题样式,Theme的样式扩展样式有我们上面的Theme.Light还有Theme.NoTitleBar、Theme.NoTitleBar.Fullscreen、Theme.Light.NoTitleBar、Theme.Light.NoTitleBar.Fullscreen、Theme.Black、......
三、自定义主题
有了上面对Theme的了解之后,下面我们通过改变标题栏来自定义主题样式,首先继承Theme,标题栏是与窗体样式(Window attributes)相关的样式,我们在Theme.xml中找到这几句代码.
<!-- Window attributes -->
<item name="windowBackground">@android:drawable/screen_background_dark</item>
<item name="windowFrame">@null</item>
<item name="windowNoTitle">false</item>
<item name="windowFullscreen">false</item>
<item name="windowIsFloating">false</item>
<item name="windowContentOverlay">@android:drawable/title_bar_shadow</item>
<item name="windowShowWallpaper">false</item>
<item name="windowTitleStyle">@android:style/WindowTitle</item>
<item name="windowTitleSize">25dip</item>
<item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Activity</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
将上面主题中Title的大小和背景覆盖
<!-- 自定义的主题样式 -->
<style name="myTheme" parent="android:Theme">
<item name="android:windowTitleBackgroundStyle">@style/myThemeStyle</item>
<item name="android:windowTitleSize">50dip</item>
</style> <!-- 主题中Title的背景样式 -->
<style name="myThemeStyle">
<item name="android:background">#FF0000</item>
</style>
默认主题样式
自定义主题样式
如果有的朋友还想改变整个应用的字体、或者风格都可以通过继承Theme覆盖原有的样式来达到自定义的效果。
Java学习交流群142979499 | |
Android学习交流群311273384 |