一、写在前面

  其实博主在之前已经对design包的各个控件都做了博文说明,无奈个人觉得理解不够深入,所以有了这篇更加深入的介绍,希望各位看官拍砖~

二、从是什么开始

  1、首先我们得知道CoordinatorLayout是什么玩意儿,到底有什么用,我们不妨看看官方文档的描述:   

    CoordinatorLayout是一个“加强版”FrameLayout,它主要有两个用途:

      1、用作应用的顶层布局管理器,也就是作为用户界面中所有UI控件的容器

      2、用作相互之间具有特定交互行为的UI控件的容器

    通过为CoordinatorLayout的子View指定Behavior,就可以实现它们之间的交互行为。 Behavior可以用来实现一系列的交互行为和布局变化,比如说侧滑菜单、可滑动删除的UI元素,以及跟随着其他UI控件移动的按钮等。

  其实总结出来就是coordinatorLayout是一个布局管理器,相当于一个增强版的FrameLayout,但是它神奇在于可以实现它的子View之间的交互行为。

  2、交互行为?

    先看个简单的效果图

    photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

    可能大家看到这,就自然能想到观察者模式,或者我昨日写的Rx模式:知识必备】RxJava+Retrofit最佳结合体验,打造懒人封装框架~

           我们的Button就是一个被观察者,TextView作为一个观察者,当Button移动的时候通知TextView,TextView就跟着移动。看看其布局:  

photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

 1 <?xml version="1.0" encoding="utf-8"?> 2 <android.support.design.widget.CoordinatorLayout 3     xmlns:android="http://schemas.android.com/apk/res/android" 4     xmlns:tools="http://schemas.android.com/tools" 5     xmlns:app="http://schemas.android.com/apk/res-auto" 6     android:id="@+id/activity_coordinator" 7     android:layout_width="match_parent" 8     android:layout_height="match_parent" 9     tools:context="com.nanchen.coordinatorlayoutdemo.CoordinatorActivity">10 11     <TextView12         android:layout_width="wrap_content"13         android:layout_height="wrap_content"14         android:text="观察者"15         app:layout_behavior=".FollowBehavior"/>16 17     <Button18         android:layout_width="wrap_content"19         android:layout_height="wrap_content"20         android:text="被观察者"21         android:layout_gravity="center"22         android:id="@+id/btn"/>23 24 </android.support.design.widget.CoordinatorLayout>

photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

    很简单,一个TextView,一个Button,外层用CoordinatorLayout,然后给我们的TextView加一个自定义的Behavior文件,内容如下:  

photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

 1 package com.nanchen.coordinatorlayoutdemo; 2  3 import android.content.Context; 4 import android.support.design.widget.CoordinatorLayout; 5 import android.util.AttributeSet; 6 import android.view.View; 7 import android.widget.Button; 8 import android.widget.TextView; 9 10 /**11  *12  * 自定义CoordinatorLayout的Behavior,泛型为观察者View(要跟着别人动的那个)13  *14  * 必须重写两个方法,layoutDependOn和onDependentViewChanged15  *16  * @author nanchen17  * @fileName CoordinatorLayoutDemo18  * @packageName com.nanchen.coordinatorlayoutdemo19  * @date 2016/12/13  10:1320  */21 22 public class FollowBehavior extends CoordinatorLayout.Behavior<TextView>{23 24     /**25      * 构造方法26      */27     public FollowBehavior(Context context, AttributeSet attrs) {28         super(context, attrs);29     }30 31     /**32      * 判断child的布局是否依赖dependency33      *34      * 根据逻辑来判断返回值,返回false表示不依赖,返回true表示依赖35      *36      * 在一个交互行为中,dependent view的变化决定了另一个相关View的行为。37      * 在这个例子中,Button就是dependent view,因为TextView跟随着它。38      * 实际上dependent view就相当于我们前面介绍的被观察者39      *40      */41     @Override42     public boolean layoutDependsOn(CoordinatorLayout parent, TextView child, View dependency) {43         return dependency instanceof Button;44     }45 46     @Override47     public boolean onDependentViewChanged(CoordinatorLayout parent, TextView child, View dependency) {48         child.setX(dependency.getX());49         child.setY(dependency.getY() + 100);50         return true;51     }52 }

photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

    重点看看其中重写的两个方法layoutDependsOn()和onDependentViewChanged()。在介绍这两个方法的作用前,我们先来介绍一下dependent view。在一个交互行为中,dependent view的变化决定了另一个相关View的行为。在这个例子中,Button就是dependent view,因为TextView跟随着它。实际上dependent view就相当于我们前面介绍的被观察者。知道了这个概念,让我们看看重写的两个方法的作用:

  • layoutDependsOn():这个方法在对界面进行布局时至少会调用一次,用来确定本次交互行为中的dependent view,在上面的代码中,当dependency是Button类的实例时返回true,就可以让系统知道布局文件中的Button就是本次交互行为中的dependent view。

  • onDependentViewChanged():当dependent view发生变化时,这个方法会被调用,参数中的child相当于本次交互行为中的观察者,观察者可以在这个方法中对被观察者的变化做出响应,从而完成一次交互行为。

  所以我们现在可以开始写Activity中的代码: 

photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

findViewById(R.id.btn).setOnTouchListener(new OnTouchListener() {
            @Override            public boolean onTouch(View view, MotionEvent motionEvent) {                if (motionEvent.getAction() == MotionEvent.ACTION_MOVE){
                    view.setX(motionEvent.getRawX()-view.getWidth()/2);
                    view.setY(motionEvent.getRawY()-view.getHeight()/2);
                }                return true;
            }
        }); 

photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

  这样一来,我们就完成了为TextView和Button设置跟随移动这个交互行为。很简单有木有,其实为CoordinatorLayout的子View设置交互行为只需三步:

  • 自定义一个继承自Behavior类的交互行为类;

  • 把观察者的layout_behavior属性设置为自定义行为类的类名;

  • 重写Behavior类的相关方法来实现我们想要的交互行为。 

值得注意的是,有些时候,并不需要我们自己来定义一个Behavior类,因为系统为我们预定义了不少Behavior类。在接下来的篇章中,我们会做出进一步的介绍。

  3、更进一步

  现在我们已经知道了怎么通过给CoordinatorLayout的子View设置Behavior来实现交互行为。现在,让我们更进一步地挖掘下CoordinatorLayout,深入了解一下隐藏在表象背后的神秘细节。

  实际上,CoordinatorLayout本身并没有做过多工作,实现交互行为的主要幕后推手是CoordinatorLayout的内部类——Behavior。通过为CoordinatorLayout的直接子View绑定一个Behavior,这个Behavior就会拦截发生在这个View上的Touch事件、嵌套滚动等。不仅如此,Behavior还能拦截对与它绑定的View的测量及布局。关于嵌套滚动,我们会在后续文章中进行详细介绍。下面我们来深入了解一下Behavior是如何做到这一切的。

  4、深入理解Behavior

    a) 拦截Touch事件

    当我们为一个CoordinatorLayout的直接子View设置了Behavior时,这个Behavior就能拦截发生在这个View上的Touch事件,那么它是如何做到的呢?实际上,CoordinatorLayout重写了onInterceptTouchEvent()方法,并在其中给Behavior开了个后门,让它能够先于View本身处理Touch事件。具体来说,CoordinatorLayout的onInterceptTouchEvent()方法中会遍历所有直接子View,对于绑定了Behavior的直接子View调用Behavior的onInterceptTouchEvent()方法,若这个方法返回true,那么后续本该由相应子View处理的Touch事件都会交由Behavior处理,而View本身表示懵逼,完全不知道发生了什么。

 

    b)拦截测量及布局

    了解了Behavior是怎养拦截Touch事件的,想必大家已经猜出来了它拦截测量及布局事件的方式——CoordinatorLayout重写了测量及布局相关的方法并为Behavior开了个后门。没错,真相就是如此。

    CoordinatorLayout在onMeasure()方法中,会遍历所有直接子View,若该子View绑定了一个Behavior,就会调用相应Behavior的onMeasureChild()方法,若此方法返回true,那么CoordinatorLayout对该子View的测量就不会进行。这样一来,Behavior就成功接管了对View的测量。

    同样,CoordinatorLayout在onLayout()方法中也做了与onMeasure()方法中相似的事,让Behavior能够接管对相关子View的布局。

 

    c) view的依赖关系的确定

    现在,我们在探究一下交互行为中的两个View之间的依赖关系是怎么确定的。我们称child为交互行为中根据另一个View的变化做出响应的那个个体,而dependent view为child所依赖的View。实际上,确立child和dependent view的依赖关系有两种方式:

  • 显式依赖:为child绑定一个Behavior,并在Behavior类的layoutDependsOn()方法中做手脚。即当传入的dependency为dependent view时返回true,这样就建立了child和dependent view之间的依赖关系。

  • 隐式依赖:通过我们最开始提到的锚(anchor)来确立。具体做法可以这样:在XML布局文件中,把child的layout_anchor属性设为dependent view的id,然后child的layout_anchorGravity属性用来描述为它想对dependent view的变化做出什么样的响应。关于这个我们会在后续篇章给出具体示例。 

  无论是隐式依赖还是显式依赖,在dependent view发生变化时,相应Behavior类的onDependentViewChanged()方法都会被调用,在这个方法中,我们可以让child做出改变以响应dependent view的变化。

 

三、玩转AppBarLayout

  实际上我们在应用中有CoordinatorLayout的地方通常都会有AppBarLayout的联用,作为同样的出自Design包的库,我们看看官方文档怎么说:

  AppBarLayout是一个垂直的LinearLayout,实现了Material Design中app bar的scrolling gestures特性。AppBarLayout的子View应该声明想要具有的“滚动行为”,这可以通过layout_scrollFlags属性或是setScrollFlags()方法来指定。
  AppBarLayout只有作为CoordinatorLayout的直接子View时才能正常工作,
  为了让AppBarLayout能够知道何时滚动其子View,我们还应该在CoordinatorLayout布局中提供一个可滚动View,我们称之为scrolling view。scrolling view和AppBarLayout之间的关联,通过将scrolling view的Behavior设为AppBarLayout.ScrollingViewBehavior来建立。

  1、一般怎么用?

  AppBar是Design的一个概念,其实我们也可以把它看做一种5.0出的ToolBar,先感受一下AppBarLayout+CoordinatorLayout的魅力。

  photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

  实际效果就是这样,当向上滑动View的时候,ToolBar会小时,向下滑动的时候,ToolBar又会出现,但别忘了,这是AppBarLayout的功能,ToolBar可办不到。由于要滑动,那么我们的AppBarLayout一定是和可以滑动的View一起使用的,比如RecyclerView,ScollView等。

  我们看看上面的到底怎么实现的:

photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

 1 <?xml version="1.0" encoding="utf-8"?> 2 <android.support.design.widget.CoordinatorLayout 3     xmlns:android="http://schemas.android.com/apk/res/android" 4     xmlns:tools="http://schemas.android.com/tools" 5     android:id="@+id/activity_coor_app_bar" 6     xmlns:app="http://schemas.android.com/apk/res-auto" 7     android:layout_width="match_parent" 8     android:layout_height="match_parent" 9     tools:context="com.nanchen.coordinatorlayoutdemo.CoorAppBarActivity">10 11     <android.support.design.widget.AppBarLayout12         android:layout_width="match_parent"13         android:layout_height="wrap_content"14         app:popupTheme="@style/ThemeOverlay.AppCompat.Light">15 16         <android.support.v7.widget.Toolbar17             android:id="@+id/toolbar"18             android:layout_width="match_parent"19             android:layout_height="?attr/actionBarSize"20             app:layout_scrollFlags="scroll|enterAlways">21 22         </android.support.v7.widget.Toolbar>23     </android.support.design.widget.AppBarLayout>24 25     <android.support.v7.widget.RecyclerView26         android:layout_width="match_parent"27         android:layout_height="match_parent"28         android:id="@+id/recycler"29         app:layout_behavior="@string/appbar_scrolling_view_behavior"/>30 31 </android.support.design.widget.CoordinatorLayout>

photoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训

  我们可以看到,上面出现了一个app:layouy_scrollFrags的自定义属性设置,这个属性可以定义我们不同的滚动行为。

  2、layout_scrollFlags

  根据官方文档,layout_scrollFlags的取值可以为以下几种。

    a)  scroll

    设成这个值的效果就好比本View和scrolling view是“一体”的。具体示例我们在上面已经给出。有一点特别需要我们的注意,为了其他的滚动行为生效,必须同时指定scroll和相应的标记,比如我们想要exitUntilCollapsed所表现的滚动行为,必须将layout_scrollFlags指定为“scroll|exitUntilCollapsed”。

    b)  exitUntilCollapsed

  当本View离开屏幕时,会被“折叠”直到达到其最小高度。我们可以这样理解这个效果:当我们开始向上滚动scrolling view时,本View会先接管滚动事件,这样本View会先进行滚动,直到滚动到了最小高度(折叠了),scrolling view才开始实际滚动。而当本View已完全折叠后,再向下滚动scrolling view,直到scrolling view顶部的内容完全显示后,本View才会开始向下滚动以显现出来。

    c) enterAlways

    当scrolling view向下滚动时,本View会一起跟着向下滚动。实际上就好比我们同时对scrolling view

网友评论

更多精彩分享

Manage dairy business efficiently with Milk Distribution Software-Java培训机构,青岛Java培训,青岛计算机培训,软件编程培训,seo优化培训,网络推广培训,网络营销培训,SEM培训,网络优化,在线营销培训,Java培训Manage dairy business efficiently with Milk Distribution Software