Behavior的使用及原理

目錄
1.事件分發(fā)介紹
2.Down雁刷、up事件的分發(fā)過(guò)程
3.onTouchListener士骤、onClickListener調(diào)用時(shí)機(jī)
4.事件攔截應(yīng)用
5.NestedScrollingParent
6.Behavior的使用
7.NestedScrollingChild接口來(lái)源
NestedScrollingParent相關(guān)接口雖然能實(shí)現(xiàn)滑動(dòng)的效果割捅,但是每次我們都需要自定義View并實(shí)現(xiàn)相關(guān)接口酸纲,還是有些麻煩组哩,因此Android又給出了Behavior供我們使用咖楣。使用時(shí)只需要先實(shí)現(xiàn)Behavior類(用于滑動(dòng)事件的消費(fèi))督笆,然后在需要消耗的布局文件中聲名,或者在類名前使用注解進(jìn)行標(biāo)注诱贿,就可實(shí)現(xiàn)娃肿,比之前的自定義View看起來(lái)更加簡(jiǎn)單,耦合更小珠十,但是前提是你只能CoordinatorLayout中使用咸作,下面就介紹下Behavior的使用及原理。
大家最熟悉的behavior使用場(chǎng)景應(yīng)該是下面的

<android.support.design.widget.CoordinatorLayout>
    <android.support.design.widget.AppBarLayout>
        <android.support.design.widget.CollapsingToolbarLayout>
            <ImageView />
        </android.support.design.widget.CollapsingToolbarLayout>
        <TextView />
    </android.support.design.widget.AppBarLayout>

    <com.example.irecyclerlib.IRecyclerView
        app:layout_behavior="@stringappbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>

但是Behavior到底是怎么起作用的呢宵睦,以及他都能實(shí)現(xiàn)什么樣的功能呢?
下面先介紹下自定義behavior的實(shí)例墅诡,然后分析下它的原理壳嚎。
本節(jié)實(shí)現(xiàn)以下效果:


效果圖

首先看下Behavior的接口:



該動(dòng)作分為兩步,首先頂部TextView移動(dòng)末早,然后RecyclerView跟隨TextView移動(dòng)烟馅,滑動(dòng)到頂端后只有RecyclerView移動(dòng),下面介紹通過(guò)Behavior來(lái)實(shí)現(xiàn)然磷。
behavior提供了兩種功能:
1.view1監(jiān)聽(tīng)另一個(gè)view2的狀態(tài)變化(大小郑趁、位置、顯示狀態(tài)等),從而使自身也進(jìn)行相應(yīng)變化姿搜,由layoutDependsOn()寡润、onDependentViewChanged()來(lái)實(shí)現(xiàn)該功能。
首先是RecyclerView的位置根據(jù)TextView的位置變化而變化舅柜。
public class RecyclerViewBehavior extends CoordinatorLayout.Behavior<RecyclerView> {
    ...
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, RecyclerView child, View dependency) {
        //所以來(lái)的對(duì)象是TextView
        return dependency instanceof TextView;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, RecyclerView child, View dependency) {
        //計(jì)算列表y坐標(biāo)梭纹,最小為0
        float y = dependency.getHeight() + dependency.getTranslationY();
        if (y < 0) {
            y = 0;
        }
        child.setY(y);//recyclerView位置進(jìn)行移動(dòng)
        return true;
    }
}

2.實(shí)現(xiàn)onNestedScroll()等接口實(shí)現(xiàn)事件的傳遞,跟我們之前講的接口NestedScrollingParent接口相似致份。

public class SampleHeaderBehavior extends CoordinatorLayout.Behavior<TextView> {
...
    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                  @NonNull TextView textView, @NonNull View recyclerView, int dx, int dy,
                                  @NonNull int[] consumed, int type) {
        super.onNestedPreScroll(coordinatorLayout, textView, recyclerView, dx, dy, consumed, type);
        if (recyclerView instanceof RecyclerView) {
            //recyclerView是否可以滑動(dòng)
            if(canScroll(textView,dy,recyclerView)){
                float finalY = textView.getTranslationY() - dy;//dy為移動(dòng)分量
                Log.d(TAG, "onNestedPreScroll: can finalY = "+finalY);
                if (dy < -textView.getHeight()) {
                    dy = -textView.getHeight();
                } else if (dy > textView.getHeight()) {
                    dy = textView.getHeight();
                }
                textView.setTranslationY(finalY);
                consumed[1] = dy;
            }
        }
    }

    private boolean canScroll(View child ,int dy,View recyclerView){
        if(dy > 0){
            //上滑
            Log.d(TAG, "canScroll->up:dy = "+dy+"  getY = "+recyclerView.getY());
            if(recyclerView.getY() > 0){
                return true;
            }else {
                return false;
            }
        }else {
            //下滑
            Log.d(TAG, "canScroll->down:dy = "+dy+"  getY = "+recyclerView.getY());
            if(recyclerView.getY() < child.getHeight()){
                return true;
            }else {
                return false;
            }
        }
    }

}

1.下面介紹下layoutDependsOn實(shí)現(xiàn)原理:
他的原理很簡(jiǎn)單变抽,就是在layoutDependsOn中設(shè)置依賴的對(duì)象(這里是TextView),然后TextView移動(dòng)時(shí)氮块,在onDependentViewChanged方法中移動(dòng)child recyclerView绍载。但是兩個(gè)方法都是在什么地方調(diào)用的呢?
(1)首先是layoutDependsOn方法的調(diào)用


CoordinatorLayout.layoutDependsOn()調(diào)用

該方法在CoordinatorLayout的onMeasure方法中調(diào)用滔蝉,然后存儲(chǔ)依賴關(guān)系mDependencySortedChildren击儡,供onDependentViewChanged使用

private void prepareChildren() {
        ...
           for (int i = 0, count = getChildCount(); i < count; i++) {
            final View view = getChildAt(i);

            final LayoutParams lp = getResolvedLayoutParams(view);
            lp.findAnchorView(this, view);

            mChildDag.addNode(view);

            // Now iterate again over the other children, adding any dependencies to the graph
            for (int j = 0; j < count; j++) {
                if (j == i) {
                    continue;
                }
                final View other = getChildAt(j);
             //調(diào)用layoutParams的dependsOn方法,下面
                if (lp.dependsOn(this, view, other)) {
                    if (!mChildDag.contains(other)) {
                        // Make sure that the other node is added
                        mChildDag.addNode(other);
                    }
                    // Now add the dependency to the graph
                    mChildDag.addEdge(other, view);
                }
            }
        }
        // Finally add the sorted graph list to our list
        //然后將依賴關(guān)系進(jìn)行存儲(chǔ)
        mDependencySortedChildren.addAll(mChildDag.getSortedList());
    }

boolean dependsOn(CoordinatorLayout parent, View child, View dependency) {
          //調(diào)用behavior的layoutDependsOn方法
            return dependency == mAnchorDirectChild
                    || shouldDodge(dependency, ViewCompat.getLayoutDirection(parent))
                    || (mBehavior != null && mBehavior.layoutDependsOn(parent, child, dependency));
        }

(2)onDependentViewChanged()的調(diào)用

onDependentViewChanged調(diào)用

該方法是在CoordinatorLayout類的onLayout方法中調(diào)用锰提。
2.behavior的onNestedPreScroll()方法的調(diào)用
image.png

recyclerView滑動(dòng)時(shí)曙痘,首先調(diào)用NestedScrollingChild2接口芳悲,通過(guò)NestedScrollingChildHelper傳遞到CoordinatorLayout的NestedScrollingParent2接口的onNestedPreScroll()方法,在該方法中再去調(diào)用behavior的onNestedPreScroll方法边坤,由此最終將事件傳遞到behavior中進(jìn)行消費(fèi)名扛。可以看出使用behavior也是在NestedScrollingParent茧痒、NestedScrollingChild接口的基礎(chǔ)上多了一層調(diào)用肮韧,將滑動(dòng)事件委托給behavior處理。
如果CoordinatorLayout與Behavior之間有其他布局旺订,還能實(shí)現(xiàn)效果么弄企?
image.png

從圖上看出,CoordinatorLayout只會(huì)尋找他的一層子布局区拳,不會(huì)再去深入獲取拘领,因此behavior不能被其他布局包裹。
上面分析完后樱调,下面對(duì)CoordinatorLayout的經(jīng)典組合進(jìn)行簡(jiǎn)單分析調(diào)用過(guò)程:


1.NestedScrollView滑動(dòng)時(shí)將Move事件傳遞給CoordinatorLayout约素,CoordinatorLayout將其傳遞給AppBarLayout的Behavior,調(diào)用其onNestedPreScroll方法進(jìn)行AppBar的滑動(dòng)笆凌。
2.AppBar滑動(dòng)時(shí)圣猎,CoordinatorLayout調(diào)用其onLayout(),onLayout時(shí)獲取view之間的依賴關(guān)系,然后將移動(dòng)距離傳遞給ScrollingViewBehavior的onDependentViewChanged()乞而,實(shí)現(xiàn)NestedScrollView跟隨AppBar的移動(dòng)送悔。
代碼詳見(jiàn) https://github.com/yanglele/AndroidSample/blob/master/app/src/main/java/com/example/yangl/androidsample/touchEvent/myCoordinatorLayout/MyCoordinatorActivity.java

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市爪模,隨后出現(xiàn)的幾起案子欠啤,更是在濱河造成了極大的恐慌,老刑警劉巖屋灌,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跪妥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡声滥,警方通過(guò)查閱死者的電腦和手機(jī)眉撵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)落塑,“玉大人纽疟,你說(shuō)我怎么就攤上這事『读蓿” “怎么了污朽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)龙考。 經(jīng)常有香客問(wèn)我蟆肆,道長(zhǎng)矾睦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任炎功,我火速辦了婚禮枚冗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蛇损。我一直安慰自己赁温,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布淤齐。 她就那樣靜靜地躺著股囊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪更啄。 梳的紋絲不亂的頭發(fā)上稚疹,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音祭务,去河邊找鬼贫堰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛待牵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播喇勋,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼缨该,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了川背?” 一聲冷哼從身側(cè)響起贰拿,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎熄云,沒(méi)想到半個(gè)月后膨更,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缴允,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年荚守,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片练般。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡矗漾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出薄料,到底是詐尸還是另有隱情敞贡,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布摄职,位于F島的核電站誊役,受9級(jí)特大地震影響获列,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛔垢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一击孩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧啦桌,春花似錦溯壶、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至板驳,卻和暖如春又跛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背若治。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工慨蓝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人端幼。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓礼烈,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親婆跑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子此熬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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