Skip to content

Latest commit

 

History

History
160 lines (118 loc) · 7.51 KB

4.CoordinatorLayout简介.md

File metadata and controls

160 lines (118 loc) · 7.51 KB

4.CoordinatorLayout简介

官方文档的第一句话就非常醒目:CoordinatorLayout is a super-powered FrameLayout,非常直白,CoordinatorLayout继承于ViewGroup,它就是一个超级强大Framelayout。说白了就是可以通过Behavior协调子View 。

<android.support.design.widget.CoordinatorLayout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/coordinatorLayout"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <View
        android:id="@+id/view_girl"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_marginLeft="200dp"
        android:background="@mipmap/make_music_voice_changer_female" />

    <View
        android:id="@+id/view_uncle"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@mipmap/make_music_voice_changer_uncle"
        app:layout_behavior="com.example.md.mdview.RunBehavior"/>
</android.support.design.widget.CoordinatorLayout>

布局中有两个子view,操作viewgirl,viewuncle也会相应跟着走,这就要写一个联动关系,用自定义Behavior实现:

public class RunBehavior extends CoordinatorLayout.Behavior<View>{

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

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        int top = dependency.getTop();
        int left = dependency.getLeft();

        ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) child.getLayoutParams();
        params.topMargin = top - 400;
        params.leftMargin = left;
        child.setLayoutParams(params);
        return true;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return true;
    }
}

CoordinatorLayout的使用核心是Behavior,Behavior就是执行你定制的动作。

在讲Behavior之前必须先理解两个概念:Child和Dependency,什么意思呢?Child当然是子View的意思了,是谁的子View呢,当然是CoordinatorLayout的子View;其实Child是指要执行动作的CoordinatorLayout的子View。而Dependency是指Child依赖的View。简而言之,就是如果Dependency这个View发生了变化,那么Child这个View就要相应发生变化。发生变化是具体发生什么变化呢?这里就要引入Behavior,Child发生变化的具体执行的代码都是放在Behavior这个类里面。

Behavior里面主要有两个方法:

  • public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency)

    判断child的布局是否依赖dependency,根据条件过滤判断返回值,返回true联动。如果返回flase不联动,即behavior不生效

  • public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency)

    当dependency发生改变时(位置、高度等),执行这个函数,返回true表示child的位置或者宽要发生变化,否则返回false

最后,别忘了在XML中通过layout_behavior来指定具体的行为:

app:layout_behavior="com.example.md.mdview.RunBehavior"

基于CoordinatorLayout联动的两点规律

1、父布局肯定是CoordinatorLayout 2、一定会设置app:layout_scrollFlagsapp:layout_behavior两个属性。

有同学可能会想一定要是 NestedScrollView 吗?

当然不是,在 CoordinatorLayout 中嵌套滑动的本质是一个 NestedScrollingChild 对象。

NestedScrollingChild 是一个接口,目前它的实现类有 4 个,分别是SwipeRefreshLayout、RecyclerView、NestedScrollView、NestedScrollView。

**滑动的View:**滑动的view官方建议使用NestedScrollView或者RecyclerView。ListView在5.0以下就没有效果了。 **高阶的使用:**高阶使用当然是自定义behavior了。

CoordinatorLayout与FloadingActionButton

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout     
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/contentView"
    android:orientation="vertical"     
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:onClick="onClick"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"/>
</RelativeLayout>

Image

可以看到FloatingActionButton会被SmackBar遮挡,为了解决遮挡的问题,就需要使用到CoordinatorLayout。 CoordinatorLayout可以监听其所有子控件的各种事件,然后自动帮助我们做出最为合理的响应。如果我们能让CoordinatorLayout监听到Snackbar的弹出事件,那么它会自动将内部的FloatingActionButton向上偏移,从而确保不会被Snackbar遮挡到。

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/contentView"
    android:orientation="vertical" 
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:id="@+id/anchorView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"/>
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_anchor="@id/anchorView"
        app:layout_anchorGravity="bottom|right"
        android:onClick="onClick"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"/>
</android.support.design.widget.CoordinatorLayout>

这样之后,悬浮按钮会在弹出Snakebar的时候自动向上偏移了Snackbar的同等高度,从而确保不会被遮挡住,当Snackbar消失的时候,悬浮按钮会自动向下偏移回到原来位置。 另外悬浮按钮的向上和向下偏移也是伴随着动画效果的,且和Snackbar完全同步,整体效果看上去特别赏心悦目。

或者我们也可以不把FloatingActionButton放到布局中,只是make()方法时传入的view是在布局中即可,我们回过头来再思考一下,刚才说的是CoordinatorLayout可以监听其所有子控件的各种事件,但是Snackbar好像并不是CoordinatorLayout的子控件吧,为什么它却可以被监听到呢?

其实道理很简单,我们在Snackbar的make()方法中传入的参数就是用来指定Snackbar是基于哪个View来触发的,如果我们传入的是FloatingActionButton本身,而FloatingActionButton是CoordinatorLayout中的子控件,因此这个事件就理所应当能被监听到了。