Drawable
在上篇筆記中簡(jiǎn)要的闡述了下個(gè)人對(duì)Drawable的理解梅誓,該篇筆記是對(duì)Drawable中的StateListDrawable實(shí)踐步清。
StateListDrawable
shape標(biāo)簽只能定義不同的形狀,而selector標(biāo)簽則可以根據(jù)控件的狀態(tài)顯示不同形狀亩码。例如一個(gè)按鈕常態(tài)與按下時(shí)的背景形狀可以完全不同行贪。有時(shí)候改變的不止是背景(包含形狀)、圖片派近,還可能是文字顏色。這些需求都可以通過(guò)selector標(biāo)簽來(lái)實(shí)現(xiàn)洁桌。
selector
selector可以有一個(gè)或者多個(gè)子標(biāo)簽item渴丸,而相應(yīng)的狀態(tài)是在item標(biāo)簽中定義的。定義的xml文件可以作為兩種資源使用:drawable和color另凌。作為drawable資源使用時(shí)谱轨,一般和shape一樣放于drawable目錄下,item必須指定android:drawable屬性吠谢;作為color資源使用時(shí)土童,則放于color目錄下,item必須指定android:color屬性工坊。
子標(biāo)簽的狀態(tài)屬性
- android:state_enabled: 設(shè)置觸摸或點(diǎn)擊事件是否可用狀態(tài)献汗,一般只在false時(shí)設(shè)置該屬性,表示不可用狀態(tài)
- android:state_pressed: 設(shè)置是否按壓狀態(tài)王污,一般在true時(shí)設(shè)置該屬性罢吃,表示已按壓狀態(tài),默認(rèn)為false
- android:state_selected: 設(shè)置是否選中狀態(tài)昭齐,true表示已選中尿招,false表示未選中
- android:state_checked: 設(shè)置是否勾選狀態(tài),主要用于CheckBox和RadioButton,true表示已被勾選就谜,false表示未被勾選
- android:state_checkable: 設(shè)置勾選是否可用狀態(tài)怪蔑,類(lèi)似state_enabled,只是state_enabled會(huì)影響觸摸或點(diǎn)擊事件丧荐,而state_checkable影響勾選事件
- android:state_focused: 設(shè)置是否獲得焦點(diǎn)狀態(tài)缆瓣,true表示獲得焦點(diǎn),默認(rèn)為false篮奄,表示未獲得焦點(diǎn)
- android:state_window_focused: 設(shè)置當(dāng)前窗口是否獲得焦點(diǎn)狀態(tài)捆愁,true表示獲得焦點(diǎn)割去,false表示未獲得焦點(diǎn)窟却,例如拉下通知欄或彈出對(duì)話框時(shí),當(dāng)前界面就會(huì)失去焦點(diǎn)呻逆;另外夸赫,ListView的ListItem獲得焦點(diǎn)時(shí)也會(huì)觸發(fā)true狀態(tài),可以理解為當(dāng)前窗口就是ListItem本身
- android:state_activated: 設(shè)置是否被激活狀態(tài)咖城,true表示被激活茬腿,false表示未激活,API Level 11及以上才支持宜雀,可通過(guò)代碼調(diào)用控件的setActivated(boolean)方法設(shè)置是否激活該控件
- android:state_hovered: 設(shè)置是否鼠標(biāo)在上面滑動(dòng)的狀態(tài)切平,true表示鼠標(biāo)在上面滑動(dòng),默認(rèn)為false辐董,API Level 14及以上才支持
- android:enterFadeDuration 狀態(tài)改變時(shí)悴品,新?tīng)顟B(tài)展示時(shí)的淡入時(shí)間,以毫秒為單位,API Level 11及以上
- android:exitFadeDuration 狀態(tài)改變時(shí)简烘,舊狀態(tài)消失時(shí)的淡出時(shí)間苔严,以毫秒為單位,API Level 11及以上
控件的drawable XML:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 窗口失去焦點(diǎn)-->
<item android:drawable="@drawable/bg_btn_lost_window_focused"
android:state_window_focused="false"/>
<!-- 不可用時(shí)-->
<item android:drawable="@drawable/bg_btn_disabled"
android:state_enabled="false"/>
<!-- 按壓時(shí) -->
<item android:drawable="@drawable/bg_btn_pressed"
android:state_pressed="true"/>
<!-- 被選中時(shí)-->
<item android:drawable="@drawable/bg_btn_selected"
android:state_selected="true"/>
<!-- 被激活時(shí)-->
<item android:drawable="@drawable/bg_btn_activated"
android:state_activated="true"/>
<!-- 獲取焦點(diǎn)時(shí)-->
<item android:drawable="@drawable/bg_btn_focused"
android:state_focused="true"/>
<!-- 默認(rèn)狀態(tài)-->
<item android:drawable="@drawable/bg_btn_normal" />
</selector>
字體的顏色XML:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 當(dāng)窗口失去焦點(diǎn)時(shí)的字體顏色-->
<item android:color="@android:color/black"
android:state_window_focused="false"/>
<!-- 控件不可用時(shí)-->
<item android:color="@android:color/background_light"
android:state_enabled="false"/>
<!-- 按壓時(shí)-->
<item android:color="@android:color/holo_blue_light"
android:state_pressed="true"/>
<!-- 被選擇時(shí)-->
<item android:color="@android:color/holo_orange_dark"
android:state_selected="true"/>
<!-- 被激活時(shí)-->
<item android:color="@android:color/holo_red_dark"
android:state_activated="true"/>
<!-- 默認(rèn)時(shí)的字體顏色-->
<item android:color="@android:color/black"/>
</selector>
在布局文件中的引用:
<Button
android:id="@+id/activate_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:background="@drawable/bg_btn_selector"
android:text="no activated"
android:textColor="@color/text_btn_selector"/>
效果顯示如下:
注意
- selector作為drawable資源時(shí),item指定android:drawable屬性孤澎,并放于drawable目錄下届氢;
- selector作為color資源時(shí),item指定android:color屬性覆旭,并放于color目錄下退子;
- color資源也可以放于drawable目錄,引用時(shí)則用@drawable來(lái)引用型将,但不推薦這么做絮供,drawable資源和color資源最好還是分開(kāi);
- android:drawable屬性除了引用@drawable資源茶敏,也可以引用@color顏色值壤靶;但android:color只能引用@color;
- item是從上往下匹配的惊搏,如果匹配到一個(gè)item那它就將采用這個(gè)item贮乳,而不是采用最佳匹配的規(guī)則忧换;所以設(shè)置默認(rèn)的狀態(tài),一定要寫(xiě)在最后向拆,如果寫(xiě)在前面亚茬,則后面所有的item都不會(huì)起作用了。
ListView的Item樣式設(shè)置
有兩種設(shè)置方式浓恳,一種是在ListView標(biāo)簽里設(shè)置android:listSelector屬性刹缝,另一種是在ListItem的布局layout里設(shè)置android:background。兩者有很大的區(qū)別:
- android:listSelector設(shè)置的ListItem默認(rèn)背景是透明的颈将,不管你在selector里怎么設(shè)置都無(wú)法改變它的背景梢夯。所以,如果想改ListItem的默認(rèn)背景晴圾,只能通過(guò)第二種方式颂砸,在ListItem的布局layout里設(shè)置android:background。
- 當(dāng)觸摸點(diǎn)擊ListItem時(shí)死姚,第一種設(shè)置方式下人乓,state_pressed、state_focused和state_window_focused設(shè)為true時(shí)都會(huì)觸發(fā)都毒,而第二種設(shè)置方式下色罚,只有state_pressed會(huì)觸發(fā)。
如果你的自定義ListViewItem中有Button账劲、ImageButton或者Checkable的子類(lèi)控件的話戳护,那么默認(rèn)focus是交給了子控件,而ListView的Item能被選中的基礎(chǔ)是它能獲取Focus涤垫。所以常常當(dāng)點(diǎn)擊item時(shí)變化的是子控件姑尺,item本身的點(diǎn)擊沒(méi)有響應(yīng)。
解決方案:
- 將Button或CheckBox換成TextView或ImageView之類(lèi)的控件
- 設(shè)置Button或CheckBox之類(lèi)的控件設(shè)置focusable屬性為false
- 設(shè)置ListItem的根布局屬性android:descendantFocusability="blocksDescendants"
第三種方法是最實(shí)用的蝠猬,descendantFocusability屬性可以設(shè)置一個(gè)控件如何處理觸摸事件切蟋,
該屬性是當(dāng)一個(gè)為view獲取焦點(diǎn)時(shí),定義viewGroup和其子控件兩者之間的關(guān)系榆芦。其值有:
- beforeDescendants:ViewGroup會(huì)優(yōu)先其子類(lèi)控件而獲取到焦點(diǎn)
- afterDescendants:ViewGroup只有當(dāng)其子類(lèi)控件不需要獲取焦點(diǎn)時(shí)才獲取焦點(diǎn)
- blocksDescendants:ViewGroup會(huì)覆蓋子類(lèi)控件而直接獲得焦點(diǎn)
其原理是這樣一來(lái)ListItem的Layout(根View)就屏蔽了所有子控件獲取Focus的權(quán)限柄粹,如此就可以順利的響應(yīng)onItemClickListener中的onItenClick()方法了。
窗口焦點(diǎn)
這次實(shí)踐中涉及到了焦點(diǎn)問(wèn)題匆绣,自己測(cè)試了下驻右。發(fā)現(xiàn)問(wèn)題比較復(fù)雜,給自己留個(gè)作業(yè)崎淳,什么是窗口焦點(diǎn)堪夭,點(diǎn)擊控件會(huì)得到焦點(diǎn),幾個(gè)控件能同時(shí)得到焦點(diǎn)嗎,焦點(diǎn)和事件傳遞有關(guān)系嗎森爽?即使我采用上面的blocksDescendants屬性恨豁,發(fā)現(xiàn)ListView的item并沒(méi)有獲取到焦點(diǎn)(通過(guò)item.isFoucsed()來(lái)判斷),這是為什么爬迟?
View如何獲取焦點(diǎn)
Android新啟動(dòng)Activity橘蜜,dialog或者其他窗體中中包含EditText, 新啟動(dòng)的activity的焦點(diǎn)默認(rèn)在EditText上付呕,這是android系統(tǒng)會(huì)彈出軟鍵盤(pán)计福,擠壓activity本來(lái)的界面,影響美觀徽职。
因此最好在新窗體創(chuàng)建時(shí)象颖,最好在onCreate()方法中將焦點(diǎn)放在另一個(gè)view上. view使用requestFocus()焦點(diǎn),但是如果讓button或者textView之類(lèi)控件直接使用requestFocus()方法活箕,則無(wú)法獲取焦點(diǎn)力麸,焦點(diǎn)會(huì)依然在editText上可款。
只能在使用View的requestFocus()方法之前調(diào)用下面2個(gè)方法育韩,view才可獲取焦點(diǎn):
- view.setFocusable(true);
- view.setFocusableInTouchMode(true);
- 然后調(diào)用 requestFocus()即可獲取焦點(diǎn)。
遇到的問(wèn)題
- 通過(guò)selector標(biāo)簽定義的color資源與value目錄下的color.xml資源有什么區(qū)別闺鲸,有時(shí)間去看看color資源講解
- 導(dǎo)入項(xiàng)目筋讨,需要更改gradle-wapper文件的最后一項(xiàng)gradle版本,進(jìn)入項(xiàng)目后可能需要更改build.gradle(module:app)里面的buildToolVersion和build.gradle(Project:name)里面的gradle依賴版本摸恍。