Behavior是Android Support Design库里面新增的布局概念,主要的作用是用来协调CoordinatorLayout里面直接Child Views之间交互行为的。

特别要注意的点是Behavior只能作用于CoordinatorLayout的直接Child View.

既然Behavior是用来协调CoordinatorLayout直接Child View的交互行为的。那Behavior是怎么工作的呢,这个也是我们本文的重点。我们准备从以下四条线路来做简单的分析。

  1. Behavior的测量和布局。(Behavior里面onMeasureChild、onLayoutChild函数)

  2. Behavior的普通触摸事件。(Behavior里面的onInterceptTouchEvent,onTouchEvent函数)

  3. Behavior的嵌套NestedScrolling触摸事件。(Behavior里面的onStartNestedScroll、onNestedScrollAccepted、onStopNestedScroll、onNestedScroll、onNestedPreScroll、onNestedFling、onNestedPreFling函数)

  4. Behavior的依赖关系。(Behavior里面的layoutDependsOn、onDependentViewChanged、onDependentViewRemoved函数)

CoordinatorLayout直接Child View的LayoutParam里面的Behavior是怎么实例化得到.有三种方式:第一种,注解设置,类似@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)的形似;第二种,java代码设置;第三种,app:layout_behavior来设置.关于Behavior的实例化这里我们就不展开来讲,有兴趣的可以参考CoordinatorLayout里Behavior简单分析里面Behavior对象是怎么被实例化的.

第一种注解方式的使用来设置默认Behavior的.

一、Behavior的测量和布局

Behavior可以引导CoordinatorLayout的直接Child View 进行测量和布局。CoordinatorLayout需要进行measure、layout的时候,都会通过Behavior询问该Behavior对应的View是否需要进行相应的测量和布局操作,如果不需要,就进行默认的行为。如果需要则按照Behavior里面编写的规则来测量和布局。这里我们只需要关注Behavior类的onMeasureChild()、onLayoutChild()两个函数。

我们以一个具体的例子来简单的解释下Behavior怎么引导CoordinatorLayout的直接Child View 进行测量和布局的.在上一篇文章Android Design Support Library 控件的使用中有一个CoordinatorLayout + RecyclerView(ViewPager里面放置的是RecyclerView) + AppBarLayout 实现AppBarLayout里面Toolbar的收缩和展开效果图的例子.如下图所示

并且他的布局文件如下

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayoutxmlns: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:background="@color/colorActivity"><android.support.design.widget.AppBarLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"><android.support.v7.widget.Toolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:background="?attr/colorPrimary"app:layout_scrollFlags="scroll"app:popupTheme="@style/ThemeOverlay.AppCompat.Light"><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center"android:textColor="@android:color/white"android:gravity="center"android:text="自定义标题"android:textSize="18sp" /></android.support.v7.widget.Toolbar><android.support.design.widget.TabLayoutandroid:id="@+id/tab_layout_title"android:layout_width="match_parent"android:layout_height="wrap_content"style="@style/AppTheme.TabStyle"app:tabMode="scrollable"app:tabGravity="fill" /></android.support.design.widget.AppBarLayout><android.support.v4.view.ViewPagerandroid:id="@+id/page_collapsing"android:layout_width="match_parent"android:layout_height="match_parent"app:layout_behavior="@string/appbar_scrolling_view_behavior" /></android.support.design.widget.CoordinatorLayout>

最外层一个CoordinatorLayout布局,并且CoordinatorLayout里面有两个直接的子View:AppBarLayout和ViewPager.其中AppBarLayout有一个默认的AppBarLayout.Behavior,同时ViewPager我们通过app:layout_behavior=”@string/appbar_scrolling_view_behavior”给设置了AppBarLayout.ScrollingViewBehavior.这样CoordinatorLayout两个直接子View都有对应的Behavior了.从界面结果出咱也能看到刚进入界面的时候ViewPager是在AppBarLayout的下面的.咱们就分析分析他是怎么做到的.肯定和测量和布局相关,那出发点肯定是CoordinatorLayout类的onMeasure()和onLayout().

CoordinatorLayout类onMeasure()函数

    @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {......for (int i = 0; i < childCount; i++) {final CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();......final CoordinatorLayout.Behavior b = lp.getBehavior();if (b == null || !b.onMeasureChild(this, child, childWidthMeasureSpec, keylineWidthUsed,childHeightMeasureSpec, 0)) {onMeasureChild(child, childWidthMeasureSpec, keylineWidthUsed,childHeightMeasureSpec, 0);}......}......}

分析可以发现如果对应的子View有对应的Behavior的时候,会先去调用Behavior里面的onMeasureChild()看Behavior有没有制定自己的测量方式.这下咱就的进入ViewPager对应的Behavior AppBarLayout.ScrollingViewBehavior里面的onMeasureChild()方法里面去瞧一瞧了,这里我们就不进去了.里面也就是一些正常的测量方法.测量完成接下来就是layout了.CoordinatorLayout类的onLayout()方法.

CoordinatorLayout类onLayout()方法

    @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {......for (int i = 0; i < childCount; i++) {final View child = mDependencySortedChildren.get(i);......final CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();final CoordinatorLayout.Behavior behavior = lp.getBehavior();if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {onLayoutChild(child, layoutDirection);}}}

同样分析可以得到有对应的Behavior就先进入到Behavior的onLayoutChild()方法了.ViewPager设置的AppBarLayout.ScrollingViewBehavior的onLayoutChild()方法里面获取得到AppBarLayout的区域,之后把ViewPager布局layout到AppBarLayout的下面.

这样咱们以一个简单的例子对Behavior的测量和布局做了一个非常简单的分析.里面很多地方也没有去深究.如果大家有什么疑问的话,可以留言.在能力范围之内的都会尽力为大家解答的.

二、Behavior的普通触摸事件

Behavior的普通触摸事件主要和Behavior里面的onInterceptTouchEvent()和onTouchEvent()两个函数相关.最终的目的也就是想把对应的触摸时间传递到Behavior对应的View里面去,让View做一些相应的处理.

父布局CoordinatorLayout产生的onInterceptTouchEvent,onTouchEvent事件都会先送到Behavior的onInterceptTouchEvent()和onTouchEvent()里面,让去问问Behavior对应的View要不要处理.你要处理就先给你处理.你不处理才轮到CoordinatorLayout来处理.关于这部分的内容之前有写过一个文章.我们就不展开讨论了.有兴趣的可以参考下CoordinatorLayout里Behavior简单分析里面Behavior的onInterceptTouchEvent + onTouchEvent一部分的分析.

三、Behavior的嵌套NestedScrolling触摸事件

关于Behavior嵌套滑动主要涉及Behavior里面的onStartNestedScroll(), onNestedScrollAccepted(), onStopNestedScroll(), onNestedScroll(), onNestedPreScroll(), onNestedFling(), onNestedPreFling() 函数.

这里我们多次提到了嵌套滑动,有兴趣的可以参考我之前写的Android 嵌套滑动分析一文的简单分析.

Behavior的嵌套NestedScrolling事件,大部分情况下是这样的.CoordinatorLayout里面另一个子View产生了嵌套滑动事件,这个事件先传递到CoordinatorLayout,然后CoordinatorLayout在把这个嵌套事件过渡到Behavior里面去.之后在让Beahaior对应的View按照实际情况做不同的处理.同样关于这部分内容的具体分析,有兴趣的可以参考下之前写的CoordinatorLayout里Behavior简单分析里面Behavior的onStartNestedScroll + onNestedScrollAccepted + onStopNestedScroll + onNestedScroll + onNestedPreScroll + onNestedFling + onNestedPreFling。嵌套滑动引起的变化部分的简单分析.

同样为了加深理解,这里还是以上文CoordinatorLayout + RecyclerView(ViewPager里面放置的是RecyclerView) + AppBarLayout 实现AppBarLayout里面Toolbar的收缩和展开效果图的例子来做一个简单的说明.这也是ViewPager里面为什么一定要放置实现了NestedScrollingChild2接口的View.这里ViewPager里面放了RecyclerView(RecyclerView实现了NestedScrollingChild接口).当RecyclerView有对应的NestedScrollingChild滑动的时候,都会先传递到CoordinatorLayout里面对应函数里面去,然后CoordinatorLayout又会原封不动的传递到Behavior对应的onStartNestedScroll(), onNestedScrollAccepted(),onStopNestedScroll(),onNestedScroll(), onNestedPreScroll(),onNestedFling(),onNestedPreFling()的函数里面去.换句话说就是传递到了AppBarLayout对应的AppBarLayout.Behavior里面去.在里面让AppBarLayout对某个View的上移和下移的处理.

四、Behavior的依赖关系

关于Behavior依赖关系对应Behavior里面的layoutDependsOn(), onDependentViewChanged(),onDependentViewRemoved()这三个函数.

Behavior的依赖指的是当前Behavior对应的View依赖于哪个View.当依赖的View有变化的时候.会调用Behavior里面对应的函数.然我们对Behavior对应的View做相应的处理.同样关于这一部分的具体分析可以参考之前写的CoordinatorLayout里Behavior简单分析里面Behavior的layoutDependsOn + onDependentViewChanged + onDependentViewRemoved。View引起的变化部分.这里我们就不重新拿出来讲了,而且里面有一个简单的例子.

为了加深理解,咱们还是以上文提到的CoordinatorLayout + RecyclerView(ViewPager里面放置的是RecyclerView) + AppBarLayout 实现AppBarLayout里面Toolbar的收缩和展开效果图的例子来做一个简单的说明哈,其实在这个里面ViewPager会依赖AppBarLayout的变化.为什么这么说呢.看ViewPager对应的AppBarLayout.ScrollingViewBehavior里面

        @Overridepublic boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {// We depend on any AppBarLayoutsreturn dependency instanceof AppBarLayout;}@Overridepublic boolean onDependentViewChanged(CoordinatorLayout parent, View child,View dependency) {offsetChildAsNeeded(parent, child, dependency);return false;}

看到了吧,如果是AppBarLayout就依赖他.并且在onDependentViewChanged函数中ViewPager也会跟着AppBarLayout的移动而移动.

五、Behavior的具体使用

5.1 BottomSheetBehavior的使用

BottomSheetBehavior:实现底部弹出框的一个Behavior,注意BottomSheetBehavior一定要配合CoordinatorLayout一起使用才有效果。

BottomSheetBehavior对应的View的状态:

状态 解释
STATE_EXPANDED bottom sheet 处于完全展开的状态:当bottom sheet的高度低于CoordinatorLayout容器时,整个bottom sheet都可见;或者CoordinatorLayout容器已经被bottom sheet填满
STATE_COLLAPSED 折叠状态(默认), bottom sheets只在底部显示一部分布局。显示高度可以通过 app:behavior_peekHeight 设置
STATE_DRAGGING 过渡状态,此时用户正在向上或者向下拖动bottom sheet
STATE_SETTLING 视图从脱离手指自由滑动到最终停下的这一小段时间
STATE_HIDDEN 默认无此状态(需要通过app:behavior_hideable 启用此状态),启用后用户将能通过向下滑动完全隐藏 bottom sheet

BottomSheetBehavior属性设置

属性 解释
app:behavior_hideable bottom sheet是否可以完全隐藏,默认为false
app:behavior_peekHeight bottom sheet为STATE_COLLAPSED(折叠)状态的时残留的高度
app:behavior_skipCollapsed 是否跳过STATE_COLLAPSED状态

BottomSheetBehavior有两种实现方式,一个之直接嵌套在布局里面,一个是通过dialog的方式弹出.两种使用方式都不难.所以我们也就以一个具体的实例来说明.效果图如下:

5.2 自定义Behavior

关于自定义Behavior,我们也实现了两个简单的效果.

5.2.1 上滑下滑的时候FloatingActionButton底部弹入或者弹出

效果图

Behavior

public class FabBottomInOutBehavior extends FloatingActionButton.Behavior {private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();private boolean mAnimatingOut = false;public FabBottomInOutBehavior() {}public FabBottomInOutBehavior(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,@NonNull FloatingActionButton child,@NonNull View directTargetChild,@NonNull View target,int axes,int type) {//需要垂直的滑动return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);}@Overridepublic void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,@NonNull FloatingActionButton child,@NonNull View target,int dxConsumed,int dyConsumed,int dxUnconsumed,int dyUnconsumed,int type) {super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);if (dyConsumed > 0 && !mAnimatingOut) {//向上滑动animateOut(child);} else if (dyConsumed < 0) {//向下滑动animateIn(child);}}private void animateOut(final FloatingActionButton button) {ViewCompat.animate(button).translationY(button.getHeight() + getMarginBottom(button)).setInterpolator(INTERPOLATOR).withLayer().setListener(new ViewPropertyAnimatorListener() {public void onAnimationStart(View view) {mAnimatingOut = true;}public void onAnimationCancel(View view) {mAnimatingOut = false;}public void onAnimationEnd(View view) {mAnimatingOut = false;}}).start();}private void animateIn(FloatingActionButton button) {ViewCompat.animate(button).translationY(0).setInterpolator(INTERPOLATOR).withLayer().setListener(null).start();}private int getMarginBottom(View v) {final ViewGroup.LayoutParams layoutParams = v.getLayoutParams();if (layoutParams instanceof ViewGroup.MarginLayoutParams) {return ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin;}return 0;}
}
5.2.2 上滑的时候以覆盖的方式盖住头部

效果图

Behavior

public class HeaderCoverBehavior extends CoordinatorLayout.Behavior<View> {public HeaderCoverBehavior() {}public HeaderCoverBehavior(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {return super.layoutDependsOn(parent, child, dependency);}@Overridepublic boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();if (params != null && params.height == CoordinatorLayout.LayoutParams.MATCH_PARENT) {child.layout(0, 0, parent.getWidth(), parent.getHeight());child.setTranslationY(getFirstChildHeight(parent));return true;}return super.onLayoutChild(parent, child, layoutDirection);}@Overridepublic boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,@NonNull View child,@NonNull View directTargetChild,@NonNull View target,int axes,int type) {return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;}@Overridepublic void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,@NonNull View child,@NonNull View target,int dx,int dy,@NonNull int[] consumed,int type) {super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);// 在这个方法里面只处理向上滑动if (dy < 0) {return;}float transY = child.getTranslationY() - dy;if (transY > 0) {child.setTranslationY(transY);consumed[1] = dy;}}@Overridepublic void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,@NonNull View child,@NonNull View target,int dxConsumed,int dyConsumed,int dxUnconsumed,int dyUnconsumed,int type) {super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);// 在这个方法里只处理向下滑动if (dyUnconsumed > 0) {return;}float transY = child.getTranslationY() - dyUnconsumed;if (transY > 0 && transY < getFirstChildHeight(coordinatorLayout)) {child.setTranslationY(transY);}}/*** 这里有优化的空间,这里纯粹的去取了第一个view的measure height 有点限制的太死了*/private int getFirstChildHeight(CoordinatorLayout coordinatorLayout) {return coordinatorLayout.getChildAt(0).getMeasuredHeight();}}

关于Behavior所要想分享的东西就这些了,如果后面自定义Behavior实现的特别有意思的效果也会第一时间分享给大家.最后上文涉及的所有实例的下载地址 https://github.com/tuacy/DesignWidget

Android CoordinatorLayout Behavior相关推荐

  1. android底部滑出view,Android CoordinatorLayout与NestedScrollView基于Behavior几行代码实现底部View滑入滑出...

    Android CoordinatorLayout与NestedScrollView基于Behavior几行代码实现底部View滑入滑出 在CoordinatorLayout的Behavior出现之前 ...

  2. android 模拟滑动app,反编译简书app和小红书app滑动效果sticky粘性头布局的实现CoordinatorLayout+behavior...

    反编译简书app和小红书app滑动效果sticky粘性头布局的实现CoordinatorLayout+behavior 小红书效果: xiaohongshuu.gif 简书效果: jianshug.g ...

  3. android 5 .0下拉回弹,自定义CoordinatorLayout.Behavior 实现下拉回弹

    先看效果 123.gif package com.tospur.exmind.testrecycerviewwithtopandbottomrefresh.refresh; import androi ...

  4. Android:自定义CoordinatorLayout.behavior 简单的仿UC首页

    CoordinatorLayout顾名思义协调布局,是用来协调该布局下的子控件,最简单地使用就是头部伸缩和折叠了,配合着TabLayout,只需要设置一下AppBarLayout子控件的layout_ ...

  5. android 触摸 卡顿,Android CoordinatorLayout(五) 严重的卡顿BUG

    这章来讲一个重大的问题,解决卡顿,我不敢保证我的方法是最优而且对所以都管用,但是至少会比之前的滑动顺畅. 如果你用我Android CoordinatorLayout(三)中写的demo,你会发现一个 ...

  6. sidhu眼中的CoordinatorLayout.Behavior(二)

    前言 在上一节sidhu眼中的CoordinatorLayout.Behavior(一)中,我们讲解了如何以通过Behavior来重写某个控件的触摸事件 可是我们只讲了如何将触摸事件抛出来,那怎么对这 ...

  7. Android 中 Behavior, NestedScrollingParent, NestedScrollingChild 关系

    NestedScroll 相关接口:NestedScrollingParent, NestScrollingChild 顶层布局需要用CoordinatorLayout, Behavior 是Coor ...

  8. sidhu眼中的CoordinatorLayout.Behavior(一)

    前言 Behavior是Android Design中推荐的布局概念,网上找了很多关于Behavior的资料,很多都是直接翻译的文档或者浅尝辄止,很多问题都没有讲明白,例如具体怎么自定义Behavio ...

  9. Android CoordinatorLayout

    Up until now we've used android CoordinatorLayout in plenty of our tutorials. Yet we haven't gone in ...

  10. 反编译简书app和小红书app滑动效果sticky粘性头布局的实现CoordinatorLayout+behavior

    反编译简书app和小红书app滑动效果sticky粘性头布局的实现CoordinatorLayout+behavior 小红书效果: 简书效果: demo效果图: github地址:https://g ...

最新文章

  1. iMeta期刊纸质版开始免费订阅(包邮)——第一期创刊收藏版
  2. qml基础学习 基础概念
  3. LeetCode 102二叉树的层序遍历103二叉树锯齿形遍历104二叉树的最大深度
  4. java ftl 标签_Freemarker-标签使用
  5. 计算机专业的第二批本科大学,第二批本科院校
  6. Python的Django框架中forms表单类的使用方法详解
  7. Pycharm虚拟环境的使用
  8. JAVA项目中找不到tomcat_Tomcat启动过程中找不到JAVA_HOME解决方法
  9. ZooKeeper在分布式应用中的作用
  10. 西安交大计算机技术考研初复试,西安交大计算机考研初试、复试信息
  11. JVM调优浅谈(转)
  12. 工具型产品的设计感想
  13. 深入理解Nginx~Nginx配置的通用语法
  14. SW转发与MAC地址表
  15. 经典C语言编程100例——题目+答案代码(1-10)
  16. 苹果悬浮球_手机轻松实现多个系统!安卓手机运行苹果iOS系统?期待!
  17. 电脑翻译,百度翻译电脑桌面版
  18. java 实现图片转化为PDF
  19. 二. Binding 详解
  20. 国内最牛的Java面试八股,不接受反驳。

热门文章

  1. Excel的N函数和VALUE函数的使用和区别
  2. STM32 Flash读写;Flash地址对应的存储内容及方式;
  3. 计算机网络链接密码,怎么连接局域网中计算机网络密码方法介绍
  4. 网络训练时使用不同学习率策略(Poly)以及学习率是如何计算
  5. 移植Python3到TQ2440(一)
  6. 讓TQ2440也用上設備樹(1)
  7. 浊音、清音爆破音的时域及频域特性
  8. Eighth Week's ARST
  9. mysql 关键字 desc_mysql数据库表字段使用DESC等关键字报错及解决方法
  10. Silverlig1.Silverlight读取外部XML加载配置-(使用WebClient读取XAP包同目录下的XML文件)...