CoordinatorLayout的Behavior初學(xué)

在使用Android設(shè)計支持庫(Android Design Support Library)時,很難避開CoordinatorLayout:設(shè)計庫中有很多視圖都需要CoordinatorLayout的支持草巡。為什么呢寥袭?實際上CoordinatorLayout本身所做的事情并不多他嫡,要是在標(biāo)準(zhǔn)框架視圖中使用它,結(jié)果也就跟普通的FrameLayout差不多。那么奇跡來自何處呢至非?完全是由于CoordinatorLayout.Behaviors的存在。只要將Behavior綁定到CoordinatorLayout的直接子元素上糠聪,就能對觸摸事件(touch events)荒椭、window insets、measurement舰蟆、layout以及嵌套滾動(nested scrolling)等動作進(jìn)行攔截趣惠。Design Library的大多功能都是借助Behavior的大量運用來實現(xiàn)的。

一.創(chuàng)建Behavior

創(chuàng)建behavior非常簡單:使用extend Behavior就可以了身害。

public class FancyBehavior<V extends View> extends CoordinatorLayout.Behavior<V> { 
     /** * Default constructor for instantiating a FancyBehavior in code. */ 
     public FancyBehavior() { } 
     /** * Default constructor for inflating a FancyBehavior from layout. 
     * * @param context The {@link Context}.
     * @param attrs The {@link AttributeSet}. 
     */ 
     public FancyBehavior(Context context, AttributeSet attrs) { 
          super(context, attrs); 
          // Extract any custom attributes out 
          // preferably prefixed with behavior_ to denote they 
          // belong to a behavior 
     }
}

注意: 這里綁定了泛型類型味悄,也就是說,可以將FancyBehavior綁定到任意視圖類上塌鸯。不過侍瑟,如果只想將Behavior綁定到特定種類的視圖上,就可以用這段代碼:

public class FancyFrameLayoutBehavior extends CoordinatorLayout.Behavior<FancyFrameLayout>

這樣一來丙猬,當(dāng)從視圖收到方法調(diào)用時涨颜,就無需再費神將大量參數(shù)轉(zhuǎn)到正確的子類中了费韭,簡單又便捷。
使用Behavior.setTag()/Behavior.getTag() 可以保存臨時數(shù)據(jù)咐低, 使用onSaveInstanceState()/onRestoreInstanceState()還可以保存Behavior相關(guān)的實例狀態(tài)揽思。雖然筆者建議要保證Behavior盡可能輕量級,不過這些方法可以讓Behavior更具狀態(tài)性见擦。

二.關(guān)聯(lián)Behavior

當(dāng)然钉汗,Behavior無法獨立完成工作,必須與實際調(diào)用的CoordinatorLayout子視圖相綁定鲤屡。具體有三種方式:通過代碼綁定损痰、在XML中綁定或者通過注釋實現(xiàn)自動綁定。

1.通過代碼綁定Behavior

如果將Behavior當(dāng)作綁定到CoordinatorLayout中每個視圖的附加數(shù)據(jù)酒来,那么發(fā)現(xiàn)Behavior實際上是存儲在各個視圖的LayoutParams中也就不足為奇了(之前有關(guān)于布局的博文)卢未。也是因此,Behavior需要綁定到CoordinatorLayout的直接子項中堰汉,因為只有那些子項會包含LayoutParams的特定Behavior子類辽社。

FancyBehavior fancyBehavior = new FancyBehavior();
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) yourView.getLayoutParams();
params.setBehavior(fancyBehavior);

2.在XML中綁定Behavior

當(dāng)然,每次都用代碼綁定的話總是有些麻煩翘鸭,正如大多自定義的LayoutParams一樣滴铅,完成這件工作也有相應(yīng)的layout_ 屬性,這里是layout_behavior屬性:

<FrameLayout 
android:layout_height=”wrap_content” 
android:layout_width=”match_parent” 
app:layout_behavior=”.FancyBehavior” />

與代碼綁定不同就乓,這里調(diào)用的總是FancyBehavior(Context context, AttributeSet attrs) 構(gòu)造函數(shù)汉匙。此外還能聲明任何自定義屬性,并將其從XML AttributeSet中提取出來生蚁,如果想要賦予開發(fā)者通過XML自定義Behavior的功能噩翠,這一點非常重要。
注意: 與父類負(fù)責(zé)解析與詮釋的Layout_屬性的命名規(guī)則相類似邦投,在Behavior中我們使用behavior_作為屬性前綴伤锚。

3.自動綁定Behavior

如果構(gòu)建了需要自定義Behavior的自定義視圖(就像Design Library中很多組件中那樣),也許你會想要默認(rèn)綁定某個behavior志衣,而無需每次手動在代碼中或XML中指定见芹。為了達(dá)到這個目的,只需在自定義視圖頂層添加簡單的注釋:

@CoordinatorLayout.DefaultBehavior(FancyFrameLayoutBehavior.class)
public class FancyFrameLayout extends FrameLayout {}

這樣蠢涝,默認(rèn)的構(gòu)造函數(shù)就會調(diào)用Behavior,與使用代碼綁定非常類似阅懦。注意:目前任何layout_behavior代表的屬性都會重寫DefaultBehavior和二。

三.攔截觸摸事件

一旦將所有behavior設(shè)置完畢,就可以準(zhǔn)備實際開工了耳胎。Behavior能做的事情之一包括攔截觸摸事件惯吕。
不用CoordinatorLayout時惕它,一般會使用各個ViewGroup的子類,Managing Touch Events training一文有提到過這個問題废登。不過有了CoordinatorLayout淹魄,通過Behavior的onInterceptTouchEvent(),將調(diào)用傳遞給它的onInterceptTouchEvent()堡距,讓Behavior獲得攔截觸摸事件的機(jī)會甲锡。通過返回為true,那么Behavior會通過onTouchEvent()接收后續(xù)的所有觸摸事件羽戒,而且無需視圖了解后續(xù)情況缤沦。SwipeDismissBehavior就是通過這樣的方式在視圖中執(zhí)行任務(wù)的。
不過更嚴(yán)重的觸摸攔截就是攔截任何交互易稠,只要在blocksInteractionBelow()中返回true就會出現(xiàn)這樣的情況缸废。當(dāng)然,在互動被攔截時也許你會希望有些視覺信號提示(以免使用者以為應(yīng)用完全不能用了)——這就是為什么blocksInteractionBelow()的默認(rèn)功能實際上依賴于getScrimOpacity()值——返回非零值會為視圖提供一層顏色遮罩(用getScrimColor()來確定顏色驶社,默認(rèn)為黑)企量,并立即禁用所有的觸摸互動。非常方便亡电。

四.攔截window insets

假設(shè)本文讀者已經(jīng)看過Why would I want to fitsSystemWindows一文届巩, 在該文中我們就fitsSystemWindows的實際作用做了深入探討,不過可歸結(jié)為:window insets需要避免在系統(tǒng)窗口(比如狀態(tài)欄和導(dǎo)航欄)之下出現(xiàn)逊抡。這里Behavior也能發(fā)揮作用:如果視圖為fitsSystemWindows=“true”姆泻,則onApplyWindowInsets()會調(diào)用綁定Behavior,且優(yōu)先級高于視圖自身冒嫡。

注意: 大多情況下拇勃,如果Behavior沒有消耗掉整個window insets,則應(yīng)當(dāng)通過ViewCompat.dispatchApplyWindowInsets() 來傳遞這個insets孝凌,以確保視圖的任何子項有機(jī)會看到這個WindowInsets方咆。

五.攔截Measurement和Layout

Measurement和layout是Android繪制視圖的關(guān)鍵組件,因此Behavior只有在onMeasureChild()和onLayoutChild()回調(diào)前攔截父視圖的measurement和layout蟀架,才能達(dá)到預(yù)計的效果瓣赂。
例如:我們采用泛型ViewGroup并為其添加一個maxWidth:

CODE1
CODE2
CODE3

編寫適用所有項目的通用Behavior非常有用,不過切記:盡量考慮在應(yīng)用內(nèi)使用behavior的辦法片拍,這樣會讓應(yīng)用更為簡單煌集。(并非所有Behavior都應(yīng)當(dāng)是泛型的!)

六.理解視圖間的依賴

上述所有功能都僅需要單個視圖便可實現(xiàn)捌省。不過Behavior的強(qiáng)大之處源自構(gòu)建視圖間的依賴苫纤,也就是說:當(dāng)另一個視圖改變時,你的Behavior會獲得回調(diào),根據(jù)外部情況來變更自身功能卷拘。
Behavior在兩種情況下會成為視圖的依賴:一種是將Behavior相應(yīng)的視圖錨定在另一個視圖上時(隱性依賴)喊废,還有一種是在layoutDependsOn()中明確返回true時。
在視圖中使用CoordinatorLayout的layout_anchor屬性栗弟,就能起到錨定的作用污筷。與layout_anchorGravity屬性一同使用,就能將兩個視圖一并有效地固定在某個位置上乍赫。例如:可以將FloatingActionButton錨定到AppBarLayout上瓣蛀,而在AppBarLayout滾動出屏幕時,F(xiàn)loatingActionButton.Behavior就會通過隱性依賴將自身隱藏起來耿焊。
無論哪種情況揪惦,當(dāng)依賴視圖被移除時,Behavior會獲得onDependentViewRemoved()的回調(diào)罗侯;而只要依賴視圖出現(xiàn)變更器腋,Behavior就會獲得onDependentViewChanged()的回調(diào)(即調(diào)整大小或自身位置)。
將視圖固定在一起的能力正是Design Library實現(xiàn)諸多炫酷功能的辦法——比如FloatingActionButton與Snackbar之間的互動钩杰。FAB的Behavior依賴于添加到CoordinatorLayout上的Snackbar實例纫塌,再通過onDependentViewChanged()回調(diào)將FAB向上移動,避免遮住Snackbar讲弄。
注意: 在添加依賴時措左,視圖總是會在依賴視圖布局后進(jìn)行布局,無視子項次序避除。

七.嵌套滾動

說到嵌套滾動怎披,有詳細(xì)介紹它的相關(guān)文章,在本文中筆者只做粗淺概述瓶摆。需要牢記這幾件事:
無需在嵌套滾動視圖中聲明依賴凉逛,因為CoordinatorLayout的每個子項都有可能接收到嵌套滾動事件。
嵌套滾動不僅可以在CoordinatorLayout的直接子項中發(fā)起群井,也能在任何子視圖(比如CoordinatorLayout的子項的子項的子項中)發(fā)起状飞。
雖然我們稱之為嵌套滾動,不過實際上包括滾動(按照滾動做1:1的位移)與滑動(flinging)兩種動作书斜。

因此诬辈,通過onStartNestedScroll()來發(fā)起感興趣的嵌套滾動事件吧。收到滾動軸(例如橫向或縱向——使它容易忽略在特定方向上的滾動)后荐吉,必須在該方向上返回true焙糟,以獲得隨后的滾動事件。
在向onStartNestedScroll()返回true之后样屠,嵌套滾動分兩步運行:
onNestedPreScroll()在滾動視圖獲得滾動事件前運行酬荞,允許相應(yīng)Behavior消耗一部分或所有的滾動事件(最后消耗的int[]是一個“外部”參數(shù)搓劫,在其中指明消耗掉的滾動)。
滾動視圖在滾動后會調(diào)用onNestedScroll()混巧,可以知道滾動了多少view,未消耗掉的(overscroll)數(shù)量又有多少勤揩。

還有類似滑動操作(盡管pre-fling調(diào)用必須要么消耗掉所有的滑動咧党,要么不消耗滑動——沒有部分消耗的選項)。
在嵌套滾動(或滑動)停止后陨亡,就能獲得onStopNestedScroll()的調(diào)用傍衡。這表示滾動結(jié)束:在下一個滾動開始前,等待重新調(diào)用onStartNestedScroll()负蠕。
舉個例子:如果想要在向下滾動時隱藏FloatingActionButton蛙埂,并在向上滾動時顯示它,只用重寫onStartNestedScroll() 和onNestedScroll()遮糖,就像在這個ScrollAwareFABBehavior中看到的那樣绣的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市欲账,隨后出現(xiàn)的幾起案子屡江,更是在濱河造成了極大的恐慌,老刑警劉巖赛不,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惩嘉,死亡現(xiàn)場離奇詭異,居然都是意外死亡踢故,警方通過查閱死者的電腦和手機(jī)文黎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來殿较,“玉大人耸峭,你說我怎么就攤上這事⌒敝” “怎么了抓艳?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帚戳。 經(jīng)常有香客問我玷或,道長,這世上最難降的妖魔是什么片任? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任偏友,我火速辦了婚禮,結(jié)果婚禮上对供,老公的妹妹穿的比我還像新娘位他。我一直安慰自己氛濒,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布鹅髓。 她就那樣靜靜地躺著舞竿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪窿冯。 梳的紋絲不亂的頭發(fā)上骗奖,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機(jī)與錄音醒串,去河邊找鬼执桌。 笑死,一個胖子當(dāng)著我的面吹牛芜赌,可吹牛的內(nèi)容都是我干的仰挣。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼缠沈,長吁一口氣:“原來是場噩夢啊……” “哼膘壶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起博烂,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤香椎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后禽篱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畜伐,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年躺率,在試婚紗的時候發(fā)現(xiàn)自己被綠了玛界。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡悼吱,死狀恐怖慎框,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情后添,我是刑警寧澤笨枯,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站遇西,受9級特大地震影響馅精,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜粱檀,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一洲敢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茄蚯,春花似錦压彭、人聲如沸睦优。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汗盘。三九已至,卻和暖如春忆畅,著一層夾襖步出監(jiān)牢的瞬間衡未,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工家凯, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人如失。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓绊诲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親褪贵。 傳聞我的和親對象是個殘疾皇子掂之,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容