V4,V7,V13等CollapsingToolbarLayoutView

Android design包中CoordinatorLayou

2017-03-09  本文已影响358人  黄海佳

最近用了一下CoordinatorLayout,做点笔记,这篇笔记只是讲原理。

一、CoordinatorLayout 是什么以及它的使用场景

Coordinator [kəʊ'ɔ:dɪneɪtə] n. 协调者;[自] 协调器;同等的人或物


1018341-f35cdb17d901108c.jpeg
CoordinatorLayout的简单介绍

我们从CoordinatorLayout的源码上翻译上大概可以知道(这里不贴源码,自己去看)

1 CoordinatorLayout是超级FrameLayout
2 它作为一个容器,它的子View之间有特殊的相互作用(交互)。

我们通过为CoordinatorLayout的子views指定Behavior,在同一个父容器下可以提供不同的交互,并且那些子view可以和另一个子view相互作用,相互影响。通过@DefaultBehavior注释,CoordinatorLayout的子view可以使用一个默认的behavior。

CoordinatorLayout的子view也许会有个anchor(锚点,即view显示在哪块区域)。

这个子view必须是CoordinatorLayout的直接子view,不能是孙子辈儿的view!

CoordinatorLayout工作原理

CoordinatorLayout的子View能够感知兄弟View的变化行为,design包的开发团队把其称为依赖(dependency)--我能感受到你的行为变化,就表示我依赖你。兄弟View之间存在一种依赖关系,CoordinatorLayout则维护这种依赖关系。
那么我们带着两个问题去探讨CoordinatorLayout:

1 兄弟View如何建立依赖关系?
2 CoordinatorLayout如何维护子View间的依赖关系?
使用场景
1 作为顶层应用程序布局装饰
2 作为协调一个或多个子View间交互的的容器。

如:通过Behavior可以用来实现各种交互和 来自滑动抽屉、滑动删除元素和按钮关联其他元素产生的额外的布局修改。

二、这篇文章涉及到的相关名词以及一些基本知识
1 先说一下View的功能
1:去测量,布局,绘制自己。
2:接收触摸事件去做出反应。
三、 Behavior

Behavior是核心设计,Behavior翻译过来就是行为。

/**
* Interaction behavior plugin for child views of {@link CoordinatorLayout}. 
* 
* <p>A Behavior implements one or more interactions that a user can take on a child view.
* These interactions may include drags, swipes, flings, or any other gestures.</p>
*
* @param <V> The View type that this Behavior operates on
*/
public static abstract class Behavior<V extends View> {
  • CoordinatorLayout的子view的交互行为插件。
Behavior如何协调CoordinatorLayout的子View的?

Behavior必须为View添加另外一种能力--就是接受CoordinatorLayout的协调通知,并对此作出反应

Paste_Image.png
回顾上面的问题:兄弟View如何建立依赖关系?

通常依赖关系的主体有两个,一个是被依赖者,另一个是依赖者。前者报告自己的行为状态的变化,后者接收这种变化,做出反应。

Behavior产生原因
  • 我们知道onInterceptTouchEvent和onTouchEvent是视图层级触摸事件分发机制的核心。这种机制简单的可以概括为:触摸事件从父视图流向子视图,在流动过程中,父视图可以观察事件,并决定是否拦截,一旦父视图决定拦截,将不会将事件在传递给子视图。这种流动是单向的,并且有单一终点--最终消耗事件的View。
1:目标视图先把事件回送到关心此事件的父视图,让其作出处理;
2:目标视图根据父视图的处理情况,调整自己的处理策略;
3:目标视图再次将自己消耗后的触摸事件传递给父视图,父视图根据需要作出处理。
Behavior的接口

看完上面我们基本知道这个CoordinatorLayout的实现原理了,先看看Behavior有哪些接口?

onStartNestedScroll,onNestedScrollAccepted,onStopNestedScroll,onNestedScroll,onNestedPreScroll,onNestedFling

发现Behavior具有NestedScrollingParent接口中的同名方法,但是不是通过直接实现NestedScrollingParent接口得到的。而CoordinatorLayout则实现了NestedScrollingParent接口。所以结论:

CoordinatorLayout 就是能接收子视图回传事件的父视图,它将收到的事件又原封不动的转发给另外一个子视图。我们现在终于可以回答最初的问题了--Behavior如何为View赋予被依赖者的能力的。答案很简单!因为它能获得第一手的触摸事件啊。它能在第一时间反应,改变自己的位置或者大小,那么整个视图层级为了达到协调,必然会依赖它--通过它的位置调整自己的位置。

Behavior实现依赖过程

依赖者View只需要告诉CoordinatorLayout它依赖哪个兄弟视图,每当它所依赖的兄弟视图行为改变的时候,CoordinatorLayout都会通知依赖者,此时依赖者就能做出相应改变了。在实现上Behavior添加了layoutDependsOn,onDependentViewChanged,onDependentViewRemoved,让View具备依赖者的能力。

最后总结Behavior

1:修改View测量,布局的能力(onMeasureChild等方法);
2:拦截或消耗触摸事件的能力(onTouchEvent等方法);
3:赋予View被依赖者的能力(NestedScrollingParent接口同名方法);
4:赋予View依赖者的能力(layoutDependsOn等);

1,2两点是对View的自身的扩展,3,4是为了配合CoordinatorLayout的行为作出的扩展。

最后解决一个问题,如何维护依赖关系?

CoordinatorLayout的每一个子View都与一个CoordinatorLayout.LayoutParams相关,当然,这是View System本来就有的设计,而Behavior则是CoordinatorLayout.LayoutParams类的成员变量,这样一来,CoordinatorLayout的每一个子View都会与一个Behavior相关。就这样Behavior无缝的整合到了View System中。最让人拍案叫绝的是,我们自定义了一个Behavoir,我们可以把它附着到任何控件类中。想想我们以前怎么做--用继承的方式实现。Behavior是赋予控件新行为最一劳永逸的方法。一次编写,任何一个控件都能使用。

事件流

1).布局事件

在CoordinatorLayout的onMeasure和onLayout方法中,会通过Behavior询问子视图是否需要进行相应操作,即执行Behavior中对应的方法,分别是onMeasureChild与onLayoutChild。这里onMeasureChild与onLayoutChild都会分别比Child的onMeasure与onLayout两方法优先执行

onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection)

2).触摸事件

触摸事件就是Behavior中的onInterceptTouchEvent与onTouchEvent。注意这里,如果Behavior对触摸事件进行了拦截,那么后续事件将不会再分发到Child View自身的触摸事件中了。而且事件由CoordinatorLayout分发下来,所以这里的touch事件都是未知View的,所以需要额外判断当前的点击事件是不是由我们的控件触发的。

3)变化事件

这里需要穿插一个判断依赖对象的过程。之前我们已经提及过在自定义Behavior时要分2种情况去考虑
(1)某个view监听另一个view的状态变化,例如大小、位置、显示状态等
(2)某个view监听滑动嵌套里的滑动状态
第二种情况我们就不需要特别的去进行判断了。重点来说说第一种。
从之前的源码阅读中我们知道,CoordinatorLayout会将其子View遍历一遍,在遍历的过程中去不断的通知所有的Behavior,这样就会导致Behavior收到不一定是我们关心的滑动事件,所以我们可以根据情况使用类型或者ID去判断依赖属性,过滤掉不是我们关心的滑动事件


上一篇下一篇

猜你喜欢

热点阅读