翻译与:Intercepting everything with CoordinatorLayout Behaviors

使用过Android Design Support Library的小伙伴应该对CoordinatorLayout比较熟悉,它可以让它的子View产生一系列联动效应,如下效果图:



但这些究竟是怎么做到的?其实CoordinatorLayout本身并没有做太多的事情,它的布局方式和FrameLayout基本相同,那么上图中我们看到的神奇的动态效果是怎么实现的呢?答案就是CoordinatorLayout.Behaviors

通过把一个Behavior附加到CoordinatorLayout的一个直接子类上面,那么这个子类就拥有了拦截CoordinatorLayout的touch events, window insets, measurement, layoutnested scrolling这一系列事件的能力。

创建一个Behavior

创建一个Behavior很简单,只要继承Behavior类即可

public class FancyBehavior<V extends View>
extends CoordinatorLayout.Behavior<V> {
/**
*当FancyBehavior在代码中被添加到子类时调用的构造函数
*/
public FancyBehavior() {
}
/**
* 当FancyBehavior是从布局文件中被添加到子类时调用的构造函数
*
* @param context The {@link Context}.
* @param attrs The {@link AttributeSet}.
*/
public FancyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
}

注意,在上面这段代码中使用了范型,此时FancyBehavior可以被添加到任何View上,如果希望FancyBehavior只被添加到特定的View子类上,可以采用如下写法:


public class FancyFrameLayoutBehavior
extends CoordinatorLayout.Behavior<FancyFrameLayout>

另外在Behavior的使用中,可以使用Behavior.setTag()/Behavior.getTag() 来保存临时性的变量,另外也可以使用onSaveInstanceState()/onRestoreInstanceState()进行数据保存。善用这些方法可以让Behavior更具状态性。

添加Behavior

Behavior本身并不做任何事情,它们需要被添加到CoordinatorLayout的直接子类才能被使用。主要有三种方法来将Behavior添加进子类,分别是程序中动态添加,Xml布局文件添加使用注解添加

程序中动态添加

FancyBehavior fancyBehavior = new FancyBehavior();
CoordinatorLayout.LayoutParams params =
(CoordinatorLayout.LayoutParams) yourView.getLayoutParams();
params.setBehavior(fancyBehavior);

在上面的这个例子中,我们在代码中向yourView添加了fancyBehavior,同时也是使用了FancyBehavior()这个构造函数,如果对FancyBehavior的构造过程需要额外的参数,可以自行重载构造函数。

Xml布局文件中添加


<FrameLayout
android:layout_height=”wrap_content”
android:layout_width=”match_parent”
app:layout_behavior=”.FancyBehavior” />

如果觉得在代码中添加会让代码变得混乱的话可以使用上面这种方式,它需要使用到CoordinatorLayout的一个自定义属性layout_behavior,属性内容是你的类名。

需要注意的是使用这种方法,FancyBehavior(Context context, AttributeSet attrs)这个构造函数会被默认调用,这样我们就可以为其自定义一些xml属性,以保证其他开发者可以通过xml来修改FancyBehavior的行为。

Note:

给布局文件添加自定义属性后,在代码中一般使用layout_的形式获取自定义属性,与此类似,使用behavior_的形式获取Behavior的自定义属性。

使用注解添加

如果你构建了一个自己的视图,并且这个视图需要一个自定义的Behavior,那么你就可以使用下面这种方式:

@CoordinatorLayout.DefaultBehavior(FancyFrameLayoutBehavior.class)
public class FancyFrameLayout extends FrameLayout {
}

使用这种方式添加的Behavior被设置成了DefaultBehavior,如果这时在Xml中使用layout_behavior的话,这个DefaultBehavior会被覆盖。

拦截触摸事件

一但你的Behavior设置到位,那么你就可以真正做一些有意义的事情了。第一个要说的就是拦截触摸事件

@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return super.onInterceptTouchEvent(parent, child, ev);
} @Override
public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return super.onTouchEvent(parent, child, ev);
}

需要拦截触摸事件的话需要重写上述两个方法,当onInterceptTouchEvent返回true的时候,那么我们的Behavior就会活的所有后续的onTouchEvent。

另外还有一个简单粗暴的方法来拦截触摸事件:

@Override
public boolean blocksInteractionBelow(CoordinatorLayout parent, V child) {
return true;
}

故名思意,当返回true的时候,我们这个视图下的其他视图将获取不到任何Touch事件。

拦截WindowInsets

如果你的视图的fitsSystemWindows属性是true,那么你的Behavior的onApplyWindowInsets()就会被调用,可以在这里优先处理WindowInsets相关问题。

Note:

如果你的视图没有消费掉WindowsInsets,那么需要调用ViewCompat.dispatchApplyWindowInsets()将其传递给子视图。

拦截Measurement和Layout

在Behavior中,可以通过重写onMeasureChild()和onLayoutChild()来拦截父视图的相关Measurement和Layout操作,比如下面的代码就是通过重写onMeasureChild()来拦截父视图的onMeasureChild(),以达到设置视图最大宽度的目的。

/*
* Copyright 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ package com.example.behaviors; import android.content.Context;
import android.content.res.TypedArray;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.view.ViewGroup; import static android.view.View.MeasureSpec; /**
* Behavior that imposes a maximum width on any ViewGroup.
*
* <p />Requires an attrs.xml of something like
*
* <pre>
* &lt;declare-styleable name="MaxWidthBehavior_Params"&gt;
* &lt;attr name="behavior_maxWidth" format="dimension"/&gt;
* &lt;/declare-styleable&gt;
* </pre>
*/
public class MaxWidthBehavior<V extends ViewGroup> extends CoordinatorLayout.Behavior<V> {
private int mMaxWidth; public MaxWidthBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.MaxWidthBehavior_Params);
mMaxWidth = a.getDimensionPixelSize(
R.styleable.MaxWidthBehavior_Params_behavior_maxWidth, 0);
a.recycle();
} @Override
public boolean onMeasureChild(CoordinatorLayout parent, V child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
if (mMaxWidth <= 0) {
// No max width means this Behavior is a no-op
return false;
}
int widthMode = MeasureSpec.getMode(parentWidthMeasureSpec);
int width = MeasureSpec.getSize(parentWidthMeasureSpec); if (widthMode == MeasureSpec.UNSPECIFIED || width > mMaxWidth) {
// Sorry to impose here, but max width is kind of a big deal
width = mMaxWidth;
widthMode = MeasureSpec.AT_MOST;
parent.onMeasureChild(child,
MeasureSpec.makeMeasureSpec(width, widthMode), widthUsed,
parentHeightMeasureSpec, heightUsed);
// We've measured the View, so CoordinatorLayout doesn't have to
return true;
} // Looks like the default measurement will work great
return false;
}
}

写一个通用的Behavior固然有用,但我们需要知道的是有时候如果你想让你的app简单一点的话完全可以把Behavior的相关功能写在自定义View的内部,没必要为了使用Behavior而是用它。

视图间的依赖

上面说到的这些功能之一来一个视图,但Behavior的强大之处不在于此,而是它可以支持视图间的相互依赖,就像一开始的那张动图一样。当一个视图发生变化后,依附在上面的Behavior就会收到一个回调,以此来实现更加丰富有用的功能。

有两种方法可以实现Behavior对视图的依赖,一种是Behavior对应的视图固定在另一个视图上,另一种是在layoutDependsOn()中返回true。

使用固定的方法也很简单,只要在Xml中添加CoordinatorLayout的自定义属性layout_anchorlayout_anchorGravity,前者用来确定目标视图,后者用来确定固定到目标视图的位置。

例如将FloatingActionButton固定到AppBarLayout后,FloatingActionButton的Behavior就会默认使用依赖关系来隐藏自己当AppBarLayout从屏幕滑出后。

通常建立了依赖关系后Behavior的以下两个方法会被激活,onDependentViewRemoved()onDependentViewChanged()

Note:

建立依赖关系的两个视图中,若被依赖的视图被从布局中移除,那么相应的那个布局也会被移除。

Nested Scrolling

关于Nested Scrolling,我们需要知道一下几点:

1.我们没有必要为了获得Nested Scrolling相关回调而去写依赖关系,CoordinatorLayout的每一个子View都能有机会捕获到Nested Scrolling事件。

2.Nested Scrolling事件不仅可以被CoordinatorLayout的直接子类捕获,也可以被它的间接子类们捕获。

3.虽然称之为nested(折叠) Scrolling,但它产生的滑动事件是1:1的。

所以,如果你需要捕获nested scrolling事件,可以在适当的时候在onStartNestedScroll()里返回true。接着你就能使用以下两个方法回调了:

1.onNestedPreScroll()会在scrolling View获得滚动事件前调用,它允许你消费部分或者全部的事件信息。

2.onNestedScroll()会在scrolling View做完滚动后调用,通过回调可以知道scrolling view滚动了多少?和它没有消耗的滚动事件。

当nested scrolling结束后,你会得到一个onStopNestedScroll()回调,说明这次的滚动事件已经结束。等待下一次的onStartNestedScroll()。

一切才只是个开端

当把上面的所有都结合起来使用的时候,就是见证奇迹的时候了。如果想了解更多相关资料,鼓励你去查看其源码,相信你能收获更多知识。

CoordinatorLayout Behaviors使用说明[翻译]的更多相关文章

  1. <Android 基础(十九)> CoordinatorLayout

    介绍 CoordinatorLayout,中文翻译,协调布局,顾名思义,此布局中的子View之间,子View与父布局之间应该是可以协调工作的,如何协调,Behavior. 今天看下Android St ...

  2. 深入理解Android开发中的CoordinatorLayout Behavior

    在使用Android设计支持库(Android Design Support Library)时,很难避开CoordinatorLayout:设计库中有很多视图都需要CoordinatorLayout ...

  3. 安卓高级6 SnackBar

    引言 文/李牧羊(简书作者) 原文链接:http://www.jianshu.com/p/2654e6bda3b1 著作权归作者所有,转载请联系作者获得授权,并标注"简书作者". ...

  4. maya中MFnMesh.h使用说明的翻译

    由于最近要修改一个maya中的deformer脚本,于是开始系统学习openMaya的一些知识,当然少不了得把一堆头文件说明看一遍.首先把MFnMesh.h这个文件翻译一下吧,不废话,上译文: 首先M ...

  5. Android Design Support Library: 学习CoordinatorLayout

    简述 CoordinatorLayout字面意思是"协调器布局",它是Design Support Library中提供的一个超级帧布局,帮助我们实现Material Design ...

  6. [置顶] 针对 CoordinatorLayout 及 Behavior 的一次细节较真

    我认真不是为了输赢,我就是认真.– 罗永浩 我一直对 Material Design 很感兴趣,每次在官网上阅读它的相关文档时,我总会有更进一步的体会.当然,Material Design 并不是仅仅 ...

  7. [翻译] ORMLite document -- How to Use Part (一)

    前言 此文档翻译于第一次学习 ORMLite 框架,如果发现当中有什么不对的地方,请指正.若翻译与原文档出现任何的不相符,请以原文档为准.原则上建议学习原英文文档. ----------------- ...

  8. “我爱背单词”beta版发布与使用说明

    我爱背单词BETA版本发布 第二轮迭代终于画上圆满句号,我们的“我爱背单词”beta版本已经发布. Beta版本说明 项目名称 我爱背单词 版本 Beta版 团队名称 北京航空航天大学计算机学院  拒 ...

  9. Android开发学习之路-Android Design Support Library使用(CoordinatorLayout的使用)

    效果图: 上面的这个图有两个效果是,一个是顶部的图片,在上滑之后会隐藏起来并且显示出一个ToolBar(ToolBar类似于ActionBar,但是只有ToolBar是兼容Material Desig ...

随机推荐

  1. 用prerender-spa-plugin插件Vue项目优化SEO做ssr服务端渲染及预渲染

    今天在做公交的时候没干,用手机看看文章,偶然发现了一个关于Vue优化seo的文章,我先是在Vue的官方文档看了一篇关于Vue做SEO优化的文章. 上面提到了nuxt.js这个框架,这个框架我做过一个小 ...

  2. Josephus排列

    思考与分析: 对于m为常数,能够用循环链表,用head,tail标志头尾指针使其易于表示循环结构.循环输出后删除结点n次,每次外层循环时.内层都固定循环m次.所以执行时间为O(mn)=O(n). 对于 ...

  3. 使用 GROUP BY WITH ROLLUP 改善统计性能

    使用 GROUP BY 的 WITH ROLLUP 字句可以检索出更多的分组聚合信息,它不仅仅能像一般的 GROUP BY 语句那样检索出各组的聚合信息,还能检索出本组类的整体聚合信息. 下面我们的例 ...

  4. Laravel自定义分页样式

    <?php namespace App\Http\Controllers; use DB; use App\Http\Controllers\Controller; class UserCont ...

  5. Windows下Python添加MySQLdb扩展模块

    [更新 2012-09-16] 这里可以下载已经打包好的EXE文件,http://sourceforge.net/projects/mysql-python/(国内需穿越才可访问) DBank备份下载 ...

  6. Oracle VM VirtualBox虚拟机导出教程

    Oracle VM VirtualBox虚拟机导出教程 | 浏览:583 | 更新:2015-01-31 11:21 1 2 3 4 5 6 7 分步阅读 有时我们需要把Oracle VM Virtu ...

  7. 利用jquery的contains实现搜索功能

    / jquery实现的搜索功能 $('#search_btn').on('click',function(){ var txt=$('#inputValue').val(); var value=$( ...

  8. 关于 web中 使用 java.net.URLEncoder.encode 要编码两次呢 , js的encodeURIComponent 同理

    因为在jsp中对中文进行了编码的时候用的是UTF-8的编码方式,而在servlet中调用request.getParameter();方法的时候使用服务器指定的原始编码格式(ISO-8859-1)自动 ...

  9. SqlServer2005删除实例

    控制面板->选中“SQL Server 2005”卸载,卸载的时候可以选择实例.

  10. ubuntu设置静态ip地址

    每次设置都忘了之前怎么设置的,所以今天记录下来. 1. 找到文件并作如下修改: sudo vim /etc/network/interfaces 修改如下部分: auto eth0iface eth0 ...