CoordinatorLayout使用详解

CoordinatorLayout

简介

CoordinatorLayout是 com.android.support:design 包下用来协调子视图之间位置和动作的布局,遵循Material Design风格。CoordinatorLayout是一个增强版的FrameLayout,他拓展了Framelayout的功能,使得子视图之间可以更好地进行协调和交互。通过给Coordintorlayout的子视图指定Behavior,就可以实现它们之间的交互行为,这也是Coordinator的主要作用。

与AppBarLayout的联合使用

AppBarLayout继承于Linearlayout,方向垂直。AppBarLayout使用app:layout_scrollFlags属性设置子控件的滚动方式。

在XMl中设置scrollFlags的值:

app:layout_scrollFlags="scroll"

在java代码中设置scrollFlags的值

TextView text= ... //确保该View是被AppBarLayout的子视图
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) text.getLayoutParams();
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);

先使用控件调用getLayoutParams方法。在调用setScrollFlags方法设置。

scrollFlags的可选值

  • scrollSCROLL_FLAG_SCROLL):使用scroll可以让AppBarLayout跟随着向上滚动,直到AppBarLayout完全隐藏,再开始滚动ScrollView。向下滚动时先将ScrollView滚动到最顶端,再开始显示AppBarLayout。必须设置此标识才能使其他的任何标识生效。如果此时图之前的任何视图没有此标志,则此值无效。

  • enterAlwaysSCROLL_FLAG_ENTER_ALWAYS):与scroll类似,向下滚动时一起滚动先将AppBarLayout完全显示,再滚动ScrollView。

  • enterAlwaysCollapsedSCROLL_FLAG_ENTER_ALWAYS_COLLAPSED):需要和enterAlways一起使用。向下滚动时,先将AppBarLayout滚动到最小高度,再滚动ScrollView,最后再滚动AppBarLayout直到完全显示。使用时要注意定义最小高度:android:minHeight="10dp"。

  • exitUntilCollapsedSCROLL_FLAG_EXIT_UNTIL_COLLAPSED):向上滚动时,AppBarLayout先向上滚动直到最小高度,然后ScrollView开始滚动。AppBarLayout不会完全退出屏幕,保留一部分。

  • snapSCROLL_FLAG_SNAP):在滚动结束时,如果视图仅部分可见,则他将自动滚到最近的边。滚动超过一半改变状态,完全消失或者完全显示。

先看一下布局的代码:

<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=".MainActivity">
​
    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
​
        <androidx.appcompat.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:title="CoordinatorLayout"
            app:subtitle="AppBarLayout"
            android:background="#636363"
            app:titleTextColor="@color/white"
            app:subtitleTextColor="@color/white"
            app:layout_scrollFlags="scroll|enterAlways"
            />
    </com.google.android.material.appbar.AppBarLayout>
​
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
<!--        app:layout_behavior="@string/appbar_scrolling_view_behavior"-->
    
        />
​
</androidx.coordinatorlayout.widget.CoordinatorLayout>

运行效果:

可以看到AppBarLayout的子控件的滚动效果已经出现了,但是RecyclerView与AppBarLayout的内容有一些重叠。毕竟CoordinatorLayout也是一种FrameLayout,都是默认再布局的左上方。

为了布局协调,我们可以给Recycler加一个behavior。(上面的代码注释掉的)

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recycler"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    />

这是一个android自带的behavior。它可以让实现NestedScrollingChild接口的子View与AppBarlayout联动,约束了子View与AppBarlayout的相对位置和滚动交互。RecyclerView就是实现了nestedScrollingChild接口的控件。

再看看效果:

关于CoordinatorLayout与Behavior的工作原理后面会简单介绍。

与CollapsingToolbarLayout的联合使用

CollapsingToolbarLayout是折叠的状态栏布局,对Toolbar进行包装。不能单独使用,要作为AppBarlayout的子View,也会用Toolbar作为它的子布局。

他也是一种FrameLayout

常用属性:

app:titleEnabled="true" //是否显示标题
app:title="CollapsingToolbarLayout" //标题内容
app:expandedTitleGravity="left|bottom" //扩展后Title的显示位置
app:collapsedTitleGravity="left" //收缩后Title的显示位置
app:contentScrim ="@color/colorPrimary" //CollapsingToolbarLayout收缩后Toolbar的背景颜色
app:scrimAnimationDuration="1200" //CollapsingToolbarLayout收缩时颜色变化的持续时间
app:scrimVisibleHeightTrigger="150dp" //颜色从可见高度的什么位置开始变化
app:statusBarScrim="@color/colorAccent" //状态颜色变化(Android 5.0)
app:layout_scrollFlags="scroll|exitUntilCollapsed" //设置滑动组件与手势之间的关系

layout_collapseMode属性的可选值有三个。

none:默认值,视图不会折叠或收缩;

pin:当折叠空间折叠时,该View会固定在顶部或者底部,不会跟随页面折叠

parallax:当可折叠空间折叠时,该View会随着页面的滚动而滚动,此时可以结合另一个属性layout_collapseParallaxMultiplier形成视差滚动的效果,即在滚动时会略微显示或隐藏一部分

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="220dp"
            android:background="@drawable/img"
            app:contentScrim="@color/black" //收缩后的背景颜色
            app:expandedTitleGravity="start|bottom" //扩展后的标题位置
            app:collapsedTitleGravity="center" //收缩后的标题位置
            app:expandedTitleTextColor="@color/white" //扩展后的标题的文本颜色
            app:collapsedTitleTextColor="@color/white" //收缩后的标题的文本颜色
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" //滚动标志
            >
            
            <androidx.appcompat.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:title="CoordinatorLayout"
                app:subtitle="AppBarLayout"
                app:titleTextColor="@color/white"
                app:subtitleTextColor="@color/white"
                app:layout_collapseMode="none" //收缩模式
                />
        </com.google.android.material.appbar.CollapsingToolbarLayout>

用这段布局代码观察三种可选值。

none

因为Toolbar设置的收缩模式时none,表示不会收缩。所以最终折叠完成后,停留在顶部的是一个完整的Toolbar。

pin

当外面的CollapsingToolbarLayout逐渐收缩,到Toolbar准备收缩时,Toolbar会直接出现在顶部,不会跟随着收缩或隐藏。

parallax

这里改写一下代码

<com.google.android.material.appbar.CollapsingToolbarLayout
    android:layout_width="match_parent"
    android:layout_height="warp_content"
    app:contentScrim="@color/black"
    app:expandedTitleGravity="start|bottom"
    app:collapsedTitleGravity="center"
    app:expandedTitleTextColor="@color/white"
    app:collapsedTitleTextColor="@color/white"
    app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
    android:minHeight="28dp"
    >
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="220dp"
        android:src="@drawable/img"
        android:scaleType="centerCrop"
​
        app:layout_collapseMode="parallax"
        app:layout_collapseParallaxMultiplier="0.5"/>
    <androidx.appcompat.widget.Toolbar
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:title="CoordinatorLayout"
        app:subtitle="AppBarLayout"
        app:titleTextColor="@color/white"
        app:subtitleTextColor="@color/white"
        app:layout_collapseMode="pin"
        />
</com.google.android.material.appbar.CollapsingToolbarLayout>

因为CollapsingToolbarLayout不能指定layout_collapseMode属性,所以使用了一个ImageView来充当背景。将ImageView的收缩模式设置成parallax,然后使用app:layout_collapseParallaxMultiplier属性。

解释:app:layout_collapseParallaxMultiplier属性会定义视图在可折叠空间中滚动时滚动速度的倍数。相对于空间的滚动速度。例如设置为1.0,该视图就会和滚动视图滚动的速度相同。设置为0.5,则该视图的滚动速度就是可折叠空间的滚动速度的一半。

可以观察到这里Toolbar的文本“Coordinatorlayout”上升的会比背景快一点。

这里下面RecyclerView的代码不改变,和AppBarLayout时一样。这里是完整的代码,可以看一下CollpaseingToolbarLayout使用的结构。

<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=".MainActivity">
​
    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/home_toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >
​
        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:contentScrim="@color/black"
            app:expandedTitleGravity="start|bottom"
            app:collapsedTitleGravity="center"
            app:expandedTitleTextColor="@color/white"
            app:collapsedTitleTextColor="@color/white"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
            android:minHeight="28dp"
            >
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="220dp"
                android:src="@drawable/img"
                android:scaleType="centerCrop"
​
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.5"/>
            <androidx.appcompat.widget.Toolbar
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:title="CoordinatorLayout"
                app:subtitle="AppBarLayout"
                app:titleTextColor="@color/white"
                app:subtitleTextColor="@color/white"
                app:layout_collapseMode="pin"
                />
        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>
    
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />
​
</androidx.coordinatorlayout.widget.CoordinatorLayout>

CollapsingToolbarLayout的折叠、展开状态监听

通过调用AppBarLayout的addOnOffsetChangedListener方法监听AppBarLayout的偏移,从而判断CollapsingToolbarLayout的折叠和展开状态。

CoordinatorLayout与Behavior的使用

CoordinatorLayout的使用的核心其实是Behavior。

使用自定义的Behavior实现如下效果:

这个”拖动“是一个Button,它可以自由拖动改变位置,那个图片是随着Button的移动改变位置,水平方向移动与Button相反,竖直方向与Button移动相同。

实现自定义Behavior的使用

首先创建一个类,使它继承CoorinatorLayout.Behavior< T>,这里泛型填入要执行动作的View的子类。然后去重写Behavior的两个方法。

public class MyBehavior extends CoordinatorLayout.Behavior<ImageView> {
    public MyBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        
    }//重写构造器
​
    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull ImageView child, @NonNull View dependency) {
        return super.layoutDependsOn(parent, child, dependency);
    }//该方法根据逻辑判断,若返回true表示child依赖于dependency,false表示不依赖。
​
    @Override
    public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull ImageView child, @NonNull View dependency) {
        return super.onDependentViewChanged(parent, child, dependency);
        
    }//该方法当dependency发生改变时(位置、宽高等),执行该方法。
    // 若该方法返回true表示child的位置或者宽高要发生改变,否则返回false。
}

在这两个重写方法中多次出现了childdependency两个变量。

解释:child指的是要执行动作的CoordinatorLayout的子View,Dependency指的是child依赖的View。

也可以说denpendency是让child改变的原因,child是随着denpendency改变而改变的View。我们手动改变denpendency的位置和大小来让child进行相应的改变。

可以在onDependentViewChanged方法中自行指定二者移动或缩放的关系。

public class MyBehavior extends CoordinatorLayout.Behavior<ImageView> {
    private int width;
    public MyBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
        //获取DisplayMetrics 是度量对象,该对象包含了与显示相关的信息,例如屏幕宽度、高度、密度等。
        width = displayMetrics.widthPixels;//获取屏幕宽度的像素值
    }//重写构造器
​
    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull ImageView child, @NonNull View dependency) {
        //如果dependency(被依赖的View)是Button的实例,说明就是我们所需要的dependency
        return dependency instanceof Button;
        //如果使用 return dependency.getId() == R.id.*; 去判断,也可以直接具体指定一个控件
​
    }//该方法根据逻辑判断,若返回true表示child依赖于dependency,false表示不依赖。
​
    @Override
    public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull ImageView child, @NonNull View dependency) {
        //每次dependency的位置发生改变,都会执行onDependentViewChanged方法
​
        //根据dependency的位置,设置child的位置
​
​
        ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) dependency.getLayoutParams();
​
        float x = width - dependency.getX() - child.getWidth() + layoutParams.getMarginEnd();
        float y = dependency.getY();
        child.setX(x);
        child.setY(y);
        return true;
    }//该方法当dependency发生改变时(位置、宽高等),执行该方法。
    // 若该方法返回true表示child的位置或者宽高要发生改变,否则返回false。
}

之后在xml文件里给作为child的视图添加layout_behavior属性。

<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=".MainActivity">
    
    <ImageView
        android:layout_width="120dp"
        android:layout_height="100dp"
        android:src="@drawable/img"
        android:scaleType="centerCrop"
        android:layout_gravity="start|center_vertical"
        android:layout_marginStart="30dp"
        app:layout_behavior=".MyBehavior"
        />
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|center_vertical"
        android:text="拖动"
        android:layout_marginEnd="30dp"
        />
​
</androidx.coordinatorlayout.widget.CoordinatorLayout>

之后在Activity中给Button设置触摸监听,让Button可以跟随手指的拖动移动。

public class MainActivity extends AppCompatActivity {
    Button button;
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
​
        button = (Button) findViewById(R.id.button);
        button.setOnTouchListener(((view, event) -> {
            if (event.getAction() == MotionEvent.ACTION_MOVE) {
                view.setX(event.getRawX()-view.getWidth()/2);
                view.setY(event.getRawY()-view.getHeight()/2);
            }        return true;
​
        }));
​
    }
}

完成了。

学习参考博客:

CoordinatorLayout的使用如此简单_coordinatelayout-CSDN博客

CoordinatorLayout详解二:-CSDN博客

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CoordinatorLayoutAndroid Support Library中新增的一个布局容器,它继承自ViewGroup类。CoordinatorLayout的主要作用是协调子View之间的交互行为,可以让子View之间相互协调,实现一些比较复杂的交互效果。 1. 特点 - CoordinatorLayout可以作为根布局,内部可以包含多个子View。 - CoordinatorLayout可以通过设置各个子View之间的依赖关系,来实现各种复杂的交互效果。 - CoordinatorLayout默认会启用一个Behavior机制,通过设置Behavior可以对子View的布局和交互行为进行控制。 - CoordinatorLayout可以对子View进行移动、缩放、旋转等操作,实现各种炫酷的效果。 2. 使用使用CoordinatorLayout时,需要注意以下几点: - CoordinatorLayout必须作为根布局。 - 子View必须设置layout_behavior属性,用于指定Behavior。 - 子View之间可以通过设置app:layout_anchor属性和app:layout_anchorGravity属性来指定依赖关系。 - 子View的Behavior必须继承自CoordinatorLayout.Behavior类。 以下是一个简单的使用CoordinatorLayout的例子: ``` <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:src="@drawable/image" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.design.widget.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="200dp" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:src="@drawable/image" android:scaleType="centerCrop" app:layout_collapseMode="parallax"/> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin"/> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.design.widget.FloatingActionButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/fab_margin" android:src="@drawable/ic_add" app:layout_anchor="@id/appbar" app:layout_anchorGravity="bottom|end"/> </android.support.design.widget.CoordinatorLayout> ``` 在这个例子中,CoordinatorLayout作为根布局,内部包含了一个ImageView、一个AppBarLayout和一个FloatingActionButton。通过设置各个View之间的依赖关系和Behavior,可以实现以下效果: - ImageView和AppBarLayout可以滚动。 - 当AppBarLayout滚动到顶部时,其中的Toolbar会固定在顶部。 - FloatingActionButton会跟随AppBarLayout一起滚动,并且会停留在AppBarLayout的底部。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值