Android Design Support之CoordinatorLayout实战

1、简介

今天来讲一下Android Design Support里面一个非常重要的组件:CoordinatorLayout。

在Material Design当中存在很多组件之间的交互,一般是以动画的形式呈现。在Design library当中引入了组件CoordinatorLayout,它是一个布局,继承自FrameLayout,通过协调调度子视图布局的形式来产生动画效果,来达到子控件之间的交互效果。

2、使用

(1)CoordinatorLayout和FloatingActionButton

这个我们在之前讲Snackbar的时候讲过,当我们把Snackbar依附于CoordinatorLayout的时候,当点击FloatingActionButton弹出Snackbar的时候,FloatingActionButton会自动上移,然后隔一段时间Snackbar消失后,FloatingActionButton就会自动归位。

   1、布局文件

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/coordinator1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.easyliu.coordinatorlayoutdemos.FloatingActionButtonActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/AppTheme.PopupOverlay" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>
2、MainActivity代码:

package com.easyliu.coordinatorlayoutdemos;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;

public class FloatingActionButtonActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_floating_action_button);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(FloatingActionButtonActivity.this.findViewById(R.id.coordinator1), "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {

                            }
                        }).show();
            }
        });
    }
}
3、效果如下:



(2)CoordinatorLayout和AppBarLayout

CoordinatorLayout最主要的用途就是和appbar一起使用。Design library当中引入了AppBarLayout,用来允许Toolbar和其他的视图比如TabLayout对其他滚动的视图做出反应,那个滚动的视图必须标记ScrollingViewBehavior。

1、AppBarLayout当中嵌套TabLayout

布局文件:

<?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:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:logo="@mipmap/ic_launcher"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:subtitle="sub Title"
            app:title="Title" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tabl_demo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpaegr_demo"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_demo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

然后把TabLayout和ViewPager关联起来,在ViewPager当中使用FragmentPagerAdapter承载Fragment实例,在每个Fragment当中放一个RecyclerView用于垂直滚动,具体代码就不贴出来了,效果如下:


当往上滚动RecyclerView的时候,Toolbar会隐藏起来。


要想达到Toolbar隐藏的效果,看以上的布局文件,有几点说明:

1、首先顶层布局必须是CoordinatorLayout

2、给需要隐藏的组件设置 app:layout_scrollFlags滚动标志,比如这里的Toolbar。这个标志有四种,如下所示:

scroll: 所有想滚动出屏幕的view都需要设置这个flag, 没有设置这个flag的view将被固定在屏幕顶部。例如TabLayout 没有设置这个值,将会停留在屏幕顶部。
enterAlways: 设置这个flag时,向下的滚动都会导致该view变为可见,启用快速“返回模式”。
enterAlwaysCollapsed: 当你的视图已经设置minHeight属性又使用此标志时,你的视图只能以最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度。
exitUntilCollapsed: 滚动退出屏幕,最后折叠在顶端。


特别需要注意的是:所有使用scroll标志的组件必须放在没有使用这个标志的组件之前,比如前面的Toolbar放在TabLayout之前,Toolbar使用了scroll标志,而TabLayout没有使用这个标志。这样可以确保所有的视图从顶部退出,留下固定的没有使用这个标志的元素在后面显示出来!


3、给可以滑动组件设置app:layout_behavior属性,这里给ViewPager设置了此属性为app:layout_behavior="@string/appbar_scrolling_view_behavior",这个是系统自带的效果。


2、AppBarLayout当中嵌套CollapsingToolbarLayout

通过把一个Toolbar直接加入到AppBarLayout当中,可以获取到两个collapsing标志:enterAlwaysCollapsed和enterAlwaysCollapsed,但是并没有详细的控制关于怎么对这个collapsing做出反应,所以在Design library当中又引入了CollapsingToolbarLayout。

首先是布局文件:

<?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/coordinator2"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="256dp"
        android:fitsSystemWindows="true">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/backdrop"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fitsSystemWindows="true"
                android:scaleType="fitXY"
                android:src="@mipmap/ic_launcher"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.7" />

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:logo="@android:drawable/ic_dialog_dialer"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:subtitle="sub Title"
                app:title="coordinatorlayout" />

        </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rcv_demo"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_demo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_dialer" />

</android.support.design.widget.CoordinatorLayout>

效果如下,这种效果在详情页面用的比较多,展现个性化内容,图像具有很强的吸引力。当往下滚动RecyclerView的时候,CollapsingToolbar会自动扩展自AppBarLayout的高度,这里定义为256dp,当往上滚动的时候,CollapsingToolbar又会折叠,最终固定为Toolbar的高度,在CollapsingToolbarLayout当中放了一个ImageView,用于承载一张图片。


关于CollapsingToolbarLayout,有几个属性说明:

1、Collapsing title:ToolBar的标题,当CollapsingToolbarLayout全屏没有折叠时,title显示的是大字体,在折叠的过程中,title不断变小到一定大小的效果。你可以调用setTitle(CharSequence)方法设置title。 
2、Content scrim:ToolBar被折叠到顶部固定时候的背景,你可以调用setContentScrim(Drawable)方法改变背景或者 在属性中使用 app:contentScrim=”?attr/colorPrimary”来改变背景。 注意这里我们不能给Toolbar设置背景,不然没有透明效果!
3、Status bar scrim:状态栏的背景,调用方法setStatusBarScrim(Drawable)。不过这个只能在Android5.0以上系统有效果。 
4、Parallax scrolling children:CollapsingToolbarLayout滑动时,子视图的视觉差,可以通过属性app:layout_collapseParallaxMultiplier=”0.6”改变。值de的范围[0.0,1.0],值越大视察越大。 
5、CollapseMode :子视图的折叠模式,在子视图设置,有两种模式。“pin”:固定模式,在折叠的时候最后固定在顶端;“parallax”:视差模式,在折叠的时候会有个视差折叠的效果。我们可以在布局中使用属性app:layout_collapseMode=”parallax”来改变。

比如这里Toolbar就设置成了"pin"模式,然后ImageView就设置成了parallax模式。

6、如果我们把CollapsingToolbarLayout的layout_scrollFlags的exitUntilCollapsed标志去掉,那么CollapsingToolbarLayout里面的控件在折叠的时候都不会显示在顶端,都会隐藏起来,忽略控件的折叠模式。

7、同时标志exitUntilCollapsed本身就包含Toolbar折叠到顶端,如果此时把Toolbar的app:layout_collapseMode设置为"parallax",那么Toolbar在折叠到顶端的时候,图标和菜单都不会显示出来,这个需要注意一下,所以一般把Toolbar的app:layout_collapseMode设置为"pin",这个读者可以自己试一下!


CoordinatorLayout 还提供了一个 layout_anchor 的属性,连同 layout_anchorGravity 一起,可以用来放置与其他视图关联在一起的悬浮视图(如 FloatingActionButton)。本例中使用FloatingActionButton。

通过下面的参数设置了FloatingActionButton的位置,两个属性共同作用使得FAB 浮动按钮也能折叠消失,展现。

app:layout_anchor="@id/appbar"
app:layout_anchorGravity="bottom|right|end"

使用CollapsingToolbarLayout实现折叠效果,需要注意3点 :
1. AppBarLayout的高度固定 
2. CollapsingToolbarLayout的子视图设置layout_collapseMode属性

3. 关联悬浮视图设置app:layout_anchor,app:layout_anchorGravity属性


3、自定义behavior

CoordinatorLayout功能如此强大,而他的神奇之处在于Behavior对象,CoordinatorLayout自己并不控制View,所有的控制权都在Behavior。前面写到了FloatingActionButton.Behavior,AppBarLayout.Behavior, AppBarLayout.ScrollingViewBehavior。 AppBarLayout中有两个Behavior,一个是拿来给它自己用的,另一个是拿来给它的兄弟结点用的。这些Behavior实现了复杂的控制功能。系统的Behavior毕竟有限,我们可以通过自定义的方式来实现自己的Behavior。


通过继承自 CoordinatorLayout.Behavior<YourView.class>来定义自己的Behavior,并在layout 文件中设置 app:layout_behavior=”com.example.app.YourView$Behavior” 来达到效果。

比如我们查看FloatingActionButton,里面有一个静态的内部类:Behavior,继承自CoordinatorLayout.Behavior<FloatingActionButton>:

 /**
     * Behavior designed for use with {@link FloatingActionButton} instances. Its main function
     * is to move {@link FloatingActionButton} views so that any displayed {@link Snackbar}s do
     * not cover them.
     */
    public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
        // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is
        // because we can use view translation properties which greatly simplifies the code.
        private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;

        private ValueAnimatorCompat mFabTranslationYAnimator;
        private float mFabTranslationY;
        private Rect mTmpRect;

然后我们就可以自定义一个View,在自定义的View当中仿照FloatingActionButton的方法定义一个静态的内部Behavior,这个Behavior继承自CoordinatorLayout.Behavior<YourView.class>,然后在自定义View的xml文件当中设置

app:layout_behavior=”com.example.app.YourView$Behavior属性来达到效果!


当然,我们也可以继承自现成的Behavior,比如现在有一个需求:当滑动RecyclerView的时候,位于右下角的FloatingActionButton自动隐藏和显示,我们可以自定义一个继承自FloatingActionButton.Behavior的类,如下所示:

public class ScrollAwareFABBehaviorDefault extends FloatingActionButton.Behavior {

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

    @Override
    public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
                                       final View directTargetChild, final View target, final int nestedScrollAxes) {
        // Ensure we react to vertical scrolling
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
                || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
                               final View target, final int dxConsumed, final int dyConsumed,
                               final int dxUnconsumed, final int dyUnconsumed) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
        if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
            // User scrolled down and the FAB is currently visible -> hide the FAB
            child.hide();
        } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
            // User scrolled up and the FAB is currently not visible -> show the FAB
            child.show();
        }
    }
}

然后在布局文件给FloatingActionButton设置相应的属性:app:layout_behavior="com.easyliu.coordinatorlayoutdemos.CollapsingToolbar.ScrollAwareFABBehaviorDefault"

如下所示:

<android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_dialer"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|end"
        app:layout_behavior="com.easyliu.coordinatorlayoutdemos.CollapsingToolbar.ScrollAwareFABBehaviorDefault" />

效果如下所示,当RecyclerView上下滑动的时候,FloatingActionButton自动显示和隐藏



以上就是CoordinatorLayout的一个大致介绍,更加详尽的说明请参考Material Design设计规范:

https://material.google.com/


到此为止,关于Android Design Support Library的介绍就告一段落了!

参考:

https://android-developers.blogspot.com/2015/05/android-design-support-library.html

http://blog.csdn.net/xyz_lmn/article/details/48055919

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/0407/4126.html





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值