CoordinatorLayout

通常說(shuō)到CoordinatorLayout浊闪,我們首先想到的就是和AppBarLayout一起使用,實(shí)現(xiàn)布局中特殊的Header效果
實(shí)際上概荷,CoordinatorLayout是可以單獨(dú)使用的秕岛,不必和AppBarLayout綁定到一起
CoordinatorLayoutLayoutParam中,有個(gè)Behavior參數(shù),CoordinatorLayout正是通過(guò)這個(gè)參數(shù)來(lái)決定子View的某些行為

Behavior

如何設(shè)置Bahavior

共有四種方式

  1. xml文件中通過(guò)app:layout_behavior="com.example.touchdemo.coordinator.behavior1.TextBehavior"
  2. 若是動(dòng)態(tài)添加的子View继薛,則通過(guò)LayoutParam#setBehavior方法
  3. 若是自定義的View修壕,則有額外兩種方式
    1. 自定義的View繼承CoordinatorLayout.AttachedBehavior接口。如BottomAppBar
    2. 自定義View的class添加CoordinatorLayout.DefaultBehavior注解遏考,如AppBarLayout

需要注意的是慈鸠,只有CoordinatorLayout的直接子View設(shè)置Behavior才有效果

Behavior可以實(shí)現(xiàn)哪些功能

Behavior有很多方法,我們可以歸為以下四類

  1. 布局相關(guān)
  2. 事件傳遞
  3. 依賴相關(guān)
  4. 嵌套滑動(dòng)

Behavior布局相關(guān)方法

public boolean onMeasureChild(@NonNull CoordinatorLayout parent, @NonNull V child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    return false;
}

public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child,
        int layoutDirection) {
    return false;
}

Behavior事件傳遞方法

public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child,
        @NonNull MotionEvent ev) {
    return false;
}

public boolean onTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child,
        @NonNull MotionEvent ev) {
    return false;
}

也就是說(shuō)灌具,如果有需要青团,我們可以通過(guò)Behavior攔截兄弟View的touch事件

Behavior依賴相關(guān)方法

public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull V child,
        @NonNull View dependency) {
    //返回true表明child依賴于dependency
    return false;
}

public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull V child,
        @NonNull View dependency) {
    //在dependency的size、position變化時(shí)咖楣,會(huì)回調(diào)該方法督笆。
    //返回true表明已處理
    return false;
}

public void onDependentViewRemoved(@NonNull CoordinatorLayout parent, @NonNull V child,
        @NonNull View dependency) {
}

Behavior嵌套滑動(dòng)相關(guān)方法

所有方法名中有Nested的方法。如果對(duì)嵌套滑動(dòng)不熟悉诱贿,需要先掌握NestedScrollingParent娃肿、NestedScrollingChild這些預(yù)備知識(shí)

CoordinatorLayout如何處理依賴關(guān)系

onMeasure方法中通過(guò)prepareChildren方法,將children的依賴關(guān)系保存到一個(gè)有向無(wú)環(huán)圖中:

private final List<View> mDependencySortedChildren = new ArrayList<>();
//有向無(wú)環(huán)圖結(jié)構(gòu)珠十,不了解沒(méi)關(guān)系料扰,知道作用就可以
private final DirectedAcyclicGraph<View> mChildDag = new DirectedAcyclicGraph<>();

private void prepareChildren() {
    //清空
    mDependencySortedChildren.clear();
    mChildDag.clear();

    //遍歷children,將依賴關(guān)系保存到mChildDag中
    for (int i = 0, count = getChildCount(); i < count; i++) {
        final View view = getChildAt(i);

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

        //將i對(duì)應(yīng)的子view添加為圖的一個(gè)節(jié)點(diǎn)
        mChildDag.addNode(view);

        for (int j = 0; j < count; j++) {
            //遍歷其他所有的子View焙蹭,查找被i依賴的j
            if (j == i) {
                continue;
            }
            final View other = getChildAt(j);
            if (lp.dependsOn(this, view, other)) {
                //view依賴于other
                if (!mChildDag.contains(other)) {
                    // 保證other被添加為圖的節(jié)點(diǎn)
                    mChildDag.addNode(other);
                }
                // 添加從view指向other的一條路徑
                mChildDag.addEdge(other, view);
                //注意這里沒(méi)有break晒杈,說(shuō)明view可以依賴于多個(gè)other
            }
        }
    }
    // mChildDag.getSortedList會(huì)將所有節(jié)點(diǎn)進(jìn)行拓?fù)渑判?    //拓?fù)渑判虻慕Y(jié)果保證 依賴的view在前,被依賴的view在后
    //然后將結(jié)果保存到mDependencySortedChildren
    mDependencySortedChildren.addAll(mChildDag.getSortedList());
    // 翻轉(zhuǎn)列表孔厉,使被依賴的view在前桐智,依賴的view在后
    //這樣當(dāng)被依賴的view有事件需要通知依賴的view時(shí),只需要從對(duì)應(yīng)的索引向后遍歷即可
    Collections.reverse(mDependencySortedChildren);
}

從以上處理依賴關(guān)系的邏輯可以得出以下結(jié)論:

  1. CoordinatorLayout只能處理直接子view的依賴烟馅,即Behavior只能應(yīng)用到直接子view上
  2. 子view不能相互依賴,否則有向無(wú)環(huán)圖無(wú)法保證
  3. 依賴是多對(duì)多的關(guān)系然磷,一個(gè)View可以被多個(gè)View依賴郑趁,也可以依賴于多個(gè)View

Behavior依賴相關(guān)方法的分發(fā)

依賴相關(guān)方法的分發(fā)在onChildViewsChanged,有三種事件類型:

static final int EVENT_PRE_DRAW = 0;
static final int EVENT_NESTED_SCROLL = 1;
static final int EVENT_VIEW_REMOVED = 2;

主要代碼不再貼出來(lái)

舉個(gè)例子

實(shí)現(xiàn)下面這樣嵌套滑動(dòng)的例子

scq1y-kbp84.gif

header是一個(gè)TextView姿搜,下面跟著一個(gè)RecyclerView寡润,布局如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".coordinator.CoordinatorActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_blue_light"
        android:gravity="center"
        android:padding="40dp"
        android:text="我可以嵌套滑動(dòng)"
        android:textColor="#fff"
        android:textSize="20dp"
        app:layout_behavior="com.example.touchdemo.coordinator.behavior1.TextBehavior" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="com.example.touchdemo.coordinator.behavior1.RecyclerViewBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

TextBehavior:

public class TextBehavior extends CoordinatorLayout.Behavior<TextView> {
    private int currentOffsetY;

    public TextBehavior() {
    }

    public TextBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                       @NonNull TextView child,
                                       @NonNull View directTargetChild,
                                       @NonNull View target,
                                       int axes,
                                       int type) {
        //在垂直方向開啟嵌套滑動(dòng)
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,
                                  @NonNull TextView child,
                                  @NonNull View target,
                                  int dx,
                                  int dy,
                                  @NonNull int[] consumed,
                                  int type) {
        //獲取可滑動(dòng)的offsetY值
        int offsetY = calOffsetY(child, dy);
        //滑動(dòng)TextView
        ViewCompat.offsetTopAndBottom(child, offsetY - currentOffsetY);

        consumed[1] = currentOffsetY - offsetY;
        currentOffsetY = offsetY;
    }

    private int calOffsetY(TextView child, int dy) {
        int offsetY = currentOffsetY - dy;
        int maxOffsetY = 0;
        int minOffsetY = -child.getHeight();
        if (offsetY < minOffsetY) {
            offsetY = minOffsetY;
        }
        if (offsetY > maxOffsetY) {
            offsetY = maxOffsetY;
        }
        return offsetY;
    }
}

RecyclerViewBehavior:

public class RecyclerViewBehavior extends CoordinatorLayout.Behavior<RecyclerView> {
    public RecyclerViewBehavior() {
    }

    public RecyclerViewBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull RecyclerView child, @NonNull View dependency) {
        //依賴于TextView
        return dependency instanceof TextView;
    }

    @Override
    public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull RecyclerView child, @NonNull View dependency) {
        //TextView布局改變時(shí),修改RecyclerView的offsetY
        ViewCompat.offsetTopAndBottom(child, dependency.getBottom() - child.getTop());
        return true;
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末舅柜,一起剝皮案震驚了整個(gè)濱河市梭纹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌致份,老刑警劉巖变抽,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绍载,警方通過(guò)查閱死者的電腦和手機(jī)诡宗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)击儡,“玉大人塔沃,你說(shuō)我怎么就攤上這事⊙舻” “怎么了蛀柴?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)矫夯。 經(jīng)常有香客問(wèn)我鸽疾,道長(zhǎng),這世上最難降的妖魔是什么茧痒? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任肮韧,我火速辦了婚禮,結(jié)果婚禮上旺订,老公的妹妹穿的比我還像新娘弄企。我一直安慰自己,他們只是感情好区拳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布拘领。 她就那樣靜靜地躺著,像睡著了一般樱调。 火紅的嫁衣襯著肌膚如雪约素。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天笆凌,我揣著相機(jī)與錄音圣猎,去河邊找鬼。 笑死乞而,一個(gè)胖子當(dāng)著我的面吹牛送悔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爪模,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼欠啤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了屋灌?” 一聲冷哼從身側(cè)響起洁段,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎共郭,沒(méi)想到半個(gè)月后祠丝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疾呻,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年纽疟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了罐韩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡污朽,死狀恐怖散吵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蟆肆,我是刑警寧澤矾睦,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站炎功,受9級(jí)特大地震影響枚冗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛇损,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一赁温、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淤齐,春花似錦股囊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至祭务,卻和暖如春内狗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背义锥。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工柳沙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拌倍。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓赂鲤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贰拿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353