參考
Attr智哀、Style和Theme詳解
Android 深入理解Android中的自定義屬性
Android 中的Theme和Style使用
總結(jié)一下Android中主題(Theme)的正確玩法
Android Design與Holo Theme詳解
為什么Material Design沒在國產(chǎn)App中流行起來?
我剛開始學(xué)Android的時候乐横,也總對這三個概念很迷惑种吸,不知道什么是屬性凶朗,什么是風(fēng)格赘那,什么是主題亚铁,它們之間又有什么關(guān)系评雌?它們在Android框架中又充當(dāng)什么角色树枫?又如何自己去定義?但隨著學(xué)習(xí)的深入景东,越發(fā)覺得這三塊內(nèi)容真是Android框架的一大神器砂轻,有時你不用改動代碼,只要換一個theme斤吐,應(yīng)用馬上煥發(fā)青春搔涝。而且也嘗試用所學(xué)內(nèi)容去寫自己的theme,不但可以讓自己的布局文件更加清晰明了和措,而且還讓自己的代碼具有更高的擴(kuò)展性庄呈,真是好處多多,希望對這塊還不了解的童鞋多多研習(xí)派阱。
一诬留、概念說明
Attr:屬性,風(fēng)格樣式的最小單元贫母;
Style:風(fēng)格故响,它是一系列Attr的集合用以定義一個View的樣式,比如height颁独、width彩届、padding等;
Theme:主題誓酒,它與Style作用一樣樟蠕,不同于Style作用于個一個單獨(dú)View贮聂,而它是作用于Activity上或是整個應(yīng)用。
TIPS:框架使用Attr的順序是:View中的Style會優(yōu)先于Activity中的Theme寨辩,Activity中的Theme會優(yōu)先于Application中的Theme吓懈,所以說你可以定義整個應(yīng)用的總體風(fēng)格,但局部風(fēng)格你也可以做出自己的調(diào)整靡狞。
二耻警、Attr的定義
其中l(wèi)ayout_width對應(yīng)到框架中的attr信息如下:
<declare-styleable name="ViewGroup_Layout">
<attr name="layout_width" format="dimension">
<enum name="fill_parent" value="-1" />
<enum name="match_parent" value="-1" />
<enum name="wrap_content" value="-2" />
</attr>
...
</declare-styleable>
從上可以看到layout_width可以使用三個枚舉值,并且其中fill_parent和match_parent的value值都為-1甸怕。做過Android開發(fā)的童鞋肯定知道甘穿,從2.2開始Android框架就推薦用match_parent代替fill_parent,而以上代碼正實(shí)現(xiàn)了兼容梢杭,因?yàn)樗鼈儗?yīng)的值都為-1温兼。
以上的textStyle的屬性信息在源碼中如下:
<attr name="textStyle">
<flag name="normal" value="0" />
<flag name="bold" value="1" />
<flag name="italic" value="2" />
</attr>
它也對應(yīng)了三個值,但這里卻使用了flag標(biāo)簽武契。細(xì)心的童鞋可能已經(jīng)明白了flag與enum的差別募判,flag表示這幾個值可以做或運(yùn)算,比如上面的textStyle咒唆,你可以疊加使用届垫,如用bold|italic表示既加粗也變成斜體,而enum只能讓你選擇其中一個值全释。
看完上例后装处,我們來試著自己自定義一個自己的屬性,在values目錄下創(chuàng)建一個attrs.xml文件恨溜,在<resources>元素里面首先申明一個自己的<declare-styleable>表示一個屬性組,再在里面加上屬性就行找前。如下我們定義一個DogStyle的屬性組糟袁,其中有三個屬性一個是dogSex,一個是dogName躺盛,dogName的格式我們設(shè)置為string项戴,最后一個是dogColor,這樣一個屬于我們自己的屬性就定義成功了槽惫。
format表示了Attr的類型周叮,可取的值有以下類型:
- color:顏色值,如#000000
- reference:引用某一資源ID界斜。如@drawable/xxx
- boolean:布爾值仿耽,true或false
- dimension:尺寸值,可以為wrap_content各薇、match_parent或是具體大邢詈亍(xx dp)
- float:浮點(diǎn)型
- fraction:百分?jǐn)?shù)
- integer:整型
- string:字符串類型
- enum:枚舉類型君躺,各個取值互斥
- flag:標(biāo)記位,各個取值可用“|”連接
attr的format根據(jù)字面意思也挺容易理解的开缎,這里我解釋下reference的用法。它用在一些可以設(shè)置引用值的情況奕删,比如@drawable/myImage、@color/myColor等伏钠。當(dāng)然format也可以進(jìn)行或運(yùn)算,一般我們定義color類型的屬性時坏怪,也一般會把format寫成format="reference|color"贝润,這樣我們不但可以設(shè)置顏色值铝宵,如#FFFFFF,還可以使用我們自己定義的狗圖片鹏秋,如@drawable/dog_pic尊蚁。
三、style的使用
為每個View重復(fù)地指定字體,顏色等屬性,無疑會增加大量的代碼,而且不利于我們后期項(xiàng)目的維護(hù),所以就引入樣式(Style) 學(xué)過web的都知道,我們可以通過css的選擇器對html中的元素進(jìn)行設(shè)置侣夷;而在UI組件中,我們可以通過style屬性來指定樣式。
style文件需要保存在res/values目錄下琴锭,文件名任意衙传,但是必須是xml文件,sytle文件的根標(biāo)記必須是<resources>蓖捶。如下我們在res/values/styles.xml中定義一個雪納瑞風(fēng)格
<style name="SchnauzerStyle">
<item name="dogName">雪納瑞</item>
<item name="dogColor">@drawable/schnauzer</item>
<item name="dogSex">boy</item>
</style>
<style name="TextView">
<item name="android:textSize">38sp</item>
<item name="android:textColor">#128</item>
<item name="android:shadowRadius">1.0</item>
<item name="android:background">#035</item>
</style>
首先我們自定義了一個View命名為DogView俊鱼,然后創(chuàng)建res/layout文件下的activity_main.xml中加入該DogView視圖,并讓該View使用SchnauzerStyle風(fēng)格并闲。代碼如下:
<cn.hadcn.test.DogView
style="@style/SchnauzerStyle"
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
<TextView
android:id="@+id/textView1"
style="@style/TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
也可以在程序中設(shè)置style
text.setTextAppearance(this, R.style.mystyle);
移步到DogView的Java代碼中帝火,我們可以通過theme的obtainStyledAttributes方法來獲得我們剛剛定義的幾個Attr屬性在Style中的內(nèi)容洒宝,如下我們舉一個獲得dogName的例子:
final Resources.Theme theme = context.getTheme();
TypedArray dogArray = theme.obtainStyledAttributes(attrs,
R.styleable.DogStyle, defStyleAttr, defStyleRes);
String name = dogArray.getString(R.styleable.DogStyle_dogName);
Log.e("dog", "name = " + name);
dogArray.recycle();
以上obtainStyledAttributes有四個入?yún)⒚染皟蓚€比較容易理解,后兩個用作指定默認(rèn)的Style靠瞎,表示如果attrs中沒有你想獲得的屬性求妹,但如果你指定了默認(rèn)Style,它會去從該默認(rèn)的Style里面找你想要的屬性父能。defStyleAttr和defStyleRes功能一樣净神,指定的資源形式不同,前者表示一個默認(rèn)的指向一個style風(fēng)格的attr屬性爱榕,而后者你可以直接傳入一個style風(fēng)格的id坡慌。注意以上定義的Style只能在這個DogView中被使用,如果你想在其他View使用跪者,就需要再在需要使用的View中增加這個Style熄求。這就是先前我們說的Style只能作用于一個View。
tips:
values-v11代表在API 11+的設(shè)備上抡四,用該目錄下的styles.xml代替res/values/styles.xml
values-v14代表在API 14+的設(shè)備上指巡,用該目錄下的styles.xml代替res/values/styles.xml
其中API 11+代表android 3.0 +
其中API 14+代表android 4.0 +
如果需要繼承其他樣式隶垮,可以使用parent屬性來指定。引用其他樣式表勉耀,如果是應(yīng)用系統(tǒng)的,需要用android:style/(其實(shí)只寫android即可至壤,不過為了好看枢纠,最好還是這么寫)打頭,如果是自己定義的樣式晋渺,用style/打頭木西。繼承時也一樣。至于是用"@"還是"?"八千,@符號表明了我們應(yīng)用的資源是前邊定義過的(或者在前一個項(xiàng)目中或者在Android 框架中)叼丑。問號?表明了我們引用的資源的值在當(dāng)前的主題當(dāng)中定義過鸠信。@引用的是之前定義好的資源當(dāng)前項(xiàng)目或者android的framework里星立。而?則是引用的當(dāng)前加載的樣式文件里室奏。意思就是說你在xml里某行定義了一個資源劲装,在下面某行需要引用這個資源時用?即可
四绒怨、Theme的使用
Theme與Style使用同一個元素標(biāo)簽<style>谦疾,區(qū)別在于所包含的屬性不同,并且使用的地方也不一樣六剥。Theme你需要設(shè)置到AndroidManifest.xml的<application>或者<activity>標(biāo)簽下,設(shè)置后疗疟,被設(shè)置的Activity或整個應(yīng)用下所有的View都可以使用該<style>里面的屬性了策彤。
比如在上例中,我們直接把SchnauzerStyle設(shè)置到<activity>標(biāo)簽中叽赊,并把布局文件中DogView元素的style="@style/SchnauzerStyle"欄位刪除必搞,以此來測試下,這個Activity下的所有View是不是可以直接使用theme中聲明的這些屬性塔橡。
<activity
android:name=".MainActivity"
android:theme="@style/SchnauzerStyle">
...
以上理論上是可行的霜第,不過運(yùn)行后,程序卻出現(xiàn)奔潰泌类,出現(xiàn)以下錯誤提示:
java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.
有些同學(xué)一眼可能就看出,因?yàn)樵谶@里Activity或Application的需要很多屬性才能工作的弹砚,而此處我們只給它傳一個SchnauzerStyle枢希,這當(dāng)然不行苞轿,所以我們需要對這個Style做下處理,讓SchnauzerStyle繼承一個系統(tǒng)主題瑟俭,如下:
<style name="SchnauzerStyle" parent="Theme.AppCompat">
<item name="dogName">雪納瑞</item>
<item name="dogColor">@drawable/schnauzer</item>
<item name="dogSex">boy</item>
</style>
這樣一個雪納瑞主題就誕生了秀睛,而在這個Activity下的所有View都可以用雪納瑞的信息了莲祸。
1.例二:
首先在res/values/themes.xml中定義Theme椭迎。
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="Theme" parent="android:Theme.Light">
<item name="android:windowFullscreen">true</item>
<item name="android:windowTitleSize">60dip</item>
<item name="android:windowTitleStyle">@style/WindowTitle</item>
<item name="android:background">#234</item>
</style>
<style name="WindowTitle">
<item name="android:singleLine">true</item>
<item name="android:shadowColor">#658</item>
<item name="android:shadowRadius">2.75</item>
</style>
</resources>
然后在AndroidManifest.xml中使用剛才定義的主題畜号。只要定義application的android:theme屬性為style/Theme即可允瞧。
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme" >
<activity
android:name="com.example.themedemo.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>
或者使用代碼setTheme(R.style.Theme)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.Theme);
setContentView(R.layout.activity_main);
}
2.主題的來源有三個
- 來自Android系統(tǒng)自帶的述暂。加上“android:”,如:android:Theme.Black
系統(tǒng)自帶主題:
API 1:
android:Theme 根主題
android:Theme.Black 背景黑色
android:Theme.Light 背景白色
android:Theme.Wallpaper 以桌面墻紙為背景
android:Theme.Translucent 透明背景
android:Theme.Panel 平板風(fēng)格
android:Theme.Dialog 對話框風(fēng)格
API 11:
android:Theme.Holo Holo根主題
android:Theme.Holo.Black Holo黑主題
android:Theme.Holo.Light Holo白主題
API 14:
Theme.DeviceDefault 設(shè)備默認(rèn)根主題
Theme.DeviceDefault.Black 設(shè)備默認(rèn)黑主題
Theme.DeviceDefault.Light 設(shè)備默認(rèn)白主題
API 21: (網(wǎng)上常說的 Android Material Design 就是要用這種主題)
Theme.Material Material根主題
Theme.Material.Light Material白主題
- 來自兼容包的(比如v7兼容包)疼蛾。不需要前綴艺配,直接:Theme.AppCompat
兼容包v7中帶的主題:
Theme.AppCompat 兼容主題的根主題
Theme.AppCompat.Black 兼容主題的黑色主題
Theme.AppCompat.Light 兼容主題的白色主題
Theme.AppCompat主題是兼容主題转唉,是什么意思呢?意思就是說如果運(yùn)行程序的手機(jī)API是21則就相當(dāng)于是Material主題麦轰,如果運(yùn)行程序的手機(jī)API是11則就相當(dāng)于是Holo主題砖织,以此類推。
兼容包v7會被Google公司不斷升級:
比如 appcompat-v7-21.0 表示升級到向 API 21 兼容
比如 appcompat-v7-23.2 表示升級到向 API 23 兼容
Android 5.0上指定用Material theme就可以喳坠。參考5.0的文檔即可茂蚓。
5.0之前的老版本,官方提供的途徑是使用appcompat v7-21 support庫晾浴。v7的意思是支持Android v7 (2.1)及以上的老版Android牍白,21是appcompat這個庫的版本。Google在文檔里也經(jīng)常直接叫做appcompat v21狸涌,不要混淆。
- 你自己寫一個主題