Behavior怎么实现复杂的视觉联动效果


这篇文章主要介绍了Behavior怎么实现复杂的视觉联动效果的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Behavior怎么实现复杂的视觉联动效果文章都会有所收获,下面我们一起来看看吧。Behavior 是谷歌 Material 设计中重要的一员,用来实现复杂的视觉联动效果。使用 Behavior 的控件需要被包裹在 CoordinateLayout 内部。Behavior 就是一个接口。Behavior 实际上就是通过将 CoordinateLayout 的布局和触摸事件传递给 Behavior 来实现的。从设计模式上讲,就一个 Behavior 而言,它是一种访问者模式,相当于将 CoordinateLayout 的布局和触摸过程对外提供的访问器;而多个 Behavior 在 CoordinateLayout 内部的事件分发则是一种责任链机制,呈现出长幼有序的状态。以 layout 过程为例,可见 Behavior 就是将子控件的布局通过 onLayoutChild() 方法对外回调了出来。控件的 behavior 优先拦截和处理 layout 事件。那 Behavior 相比于我们直接覆写触摸事件的形式处理手势有什么优点呢?其优点在于,我们可以将页面的布局、触摸和滑动等事件封装到 Behavior 接口的实现类中以达到交互逻辑的复用和解耦的目的。Behavior 接口定义了许多方法,用于将 CoordinateLayout 的布局、测量和事件分发事件向外传递。这里我根据其作用将其归纳为以下几组。首先是 Behavior 和 LayoutParams 关联和接触绑定时回调的方法。它们被回调的世纪分别是,onAttachedToLayoutParams:LayoutParams 的构造函数中回调onDetachedFromLayoutParams:调用 LayoutParams 的 setBehavior,用一个新的 Behavior 覆盖旧的 Behavior 时回调然后是跟 scrim color 相关的方法,这些方法会在 CoordinateLayout 的绘制过程中被调用。主要是跟绘制相关的,即用来对指定的 child 进行着色。这里的 child 是指该 Behavior 所关联的控件,parent 就是指包裹这个 child 的最外层的 CoordinatorLayout. 后面的方法都是如此。然后一组方法是用来将 CoordinatorLayout 的测量和布局过程对外回调。不论是测量还是布局的回调方法,优先级都是回调方法优先。也就是回调方法可以通过返回 true 拦截 CoordinatorLayout 的逻辑。另外,CoordinatorLayout 里使用 Behavior 的时候只会从直系子控件上读取,所以,子控件的子控件上即便有 Behavior 也不会被拦截处理。所以,在一般使用 CoordinatorLayout 的时候,如果我们需要在某个控件上使用 Behavior,都是将其作为 CoordinatorLayout 的直系子控件。还要注意,一个 CoordinatorLayout 的直系子控件包含多个 Behavior 的时候,这些 Behavior 被回调的先后顺序和它们在 CoordinatorLayout 里布局的先后顺序一致。也就是说,排序在前的子控件优先拦截和处理事件。这和中国古代的王位继承制差不多。接下来的一组方法用来描述子控件之间的依赖关系。它的作用原理是,当 CoordinatorLayout 发生以下三类事件NestedScroll 滚动事件,通过 onNestedScroll() 获取(后面会分析这个事件工作原理)PreDraw 事件,通过 ViewTreeObserver.OnPreDrawListener 获取到该事件控件被移除事件,通过 OnHierarchyChangeListener 获取到该事件的时候会使用 layoutDependsOn() 方法,针对 CoordinatorLayout 的每个子控件,判断其他子控件与其是否构成依赖关系。如果构成了依赖关系,就回调其对应的 Behavior 的 onDependentViewChanged() 或者 onDependentViewRemoved() 方法。然后是与窗口变化和状态保存与恢复相关的事件。这些事件一般不会用到。以上是 Behavior 内定义的一些方法。Behavior 主要的用途还是用来做触摸事件的分发。这里,我们来重点关注和触摸事件分发相关的方法。首先我们来回顾传统的事件分发机制。当 window 将触摸事件交给 DecorView 之后,触摸事件在 ViewGroup 和 View 之间传递遵循如下模型,所以,子控件可以通过调用父控件的 requestDisallowInterceptTouchEvent() 方法不让父控件拦截事件。但是这种拦截机制完全是基于默认的实现逻辑。如果父控件修改了 requestDisallowInterceptTouchEvent() 方法或者 dispatchTouchEvent() 方法的逻辑,子控件的约束效果是无效的。父控件通过 onInterceptTouchEvent() 拦截事件只能拦截部分事件。相比于父控件,子控件的事件分发则简单得多。首先是先将事件交给自定义的 mOnTouchListener 来处理,其没有消费才将其交给默认的 onTouchEvent 来处理。在 onTouchEvent 里则会判断事件的类型,比如点击和长按之类的,而且可以看到系统源码在判断具体的事件类型的时候使用了 post Runnable 的方式。在父控件中如果子控件没有处理,则父控件将会走 View 的 dispatchTouchEvent() 逻辑,也就是去判断事件的类型来消费了。在 Behavior 中定义了两个与触摸事件分发相关的方法,对照上面的事件分发机制中 onInterceptTouchEvent 和 onTouchEvent 的逻辑,这里的 Behavior 的拦截逻辑是:CoordinatorLayout 按照 Behavior 的出现顺序进行遍历,先走 CoordinatorLayout 的 onInterceptTouchEvent,如果一个 Behavior 的 onInterceptTouchEvent 拦截了该事件,则会记录拦截该事件的 View 并给其他 Behavior 的 onInterceptTouchEvent 发送给一个 Cancel 类型的触摸事件。然后,在 CoordinatorLayout 的 onTouchEvent 方法中会执行该 View 对应的 Behavior 的 onTouchEvent 方法。安卓在 5.0 上引入了 NestedScrolling 机制。之所以引入该事件是因为传统的事件分发机制 MOVE 事件当父控件拦截了之后就无法再交给子 View. 而 NestedScrolling 机制可以指定在一个滑动事件中,父控件和子控件分别消费多少。比如,在一个向上的滑动事件中,我们需要 toolbar 先向上滑动 50dp,然后列表再向上滑动。此时,我们可以先让 toolbar 消费 50dp 的事件,剩下的再交给列表处理,让其向上滑动 6dp 的距离。在 NestedScrolling 机制中定义了 NestedScrollingChildNestedScrollingParent 两个接口(为了支持更多功能后续又定义了 NestedScrollingChild2 和 NestedScrollingChild3 等接口)。外部容器通常实现 NestedScrollingParent 接口,而子控件通常实现 NestedScrollingChild 接口。在常规的事件分发机制中,子控件(比如 RecyclerView 或者 NestedScrollView )会在 Move 事件中找到父控件,如果该父控件实现了 NestedScrollingParent 接口,就会通知该父控件发生了滑动事件。然后,父控件可以对滑动事件进行进一步的分发。以 RecyclerVi免费云主机域名ew 为例,这里 dispatchNestedPreScroll() 就是滑动事件的分发逻辑,它最终会走到 ViewParentCompat 的 onNestedPreScroll() 方法,并在该方法中向上交给父控件进行分发。代码如下,在 CoordinatorLayout 中,与 NestedScrolling 机制相关的方法主要分成 scroll 和 fling 两类。以 scroll 类型的事件为例,其工作的原理:CoordinatorLayout 中会对子控件进行遍历,然后将对应的事件传递给子控件的 Behavior (若有)的对应方法。对于滑动类型的事件,在滑动事件传递的时候先传递 onStartNestedScroll 事件,用来判断某个 View 是否拦截滑动事件。而在 CoordinatorLayout 中,会交给 Beahvior 判断是否处理该事件。然后 CoordinatorLayout 会讲该 Behavior 是否拦截该事件的状态记录到对应的 View 的 LayoutParam. 然后,当 CoordinatorLayout 的 onNestedPreScroll 被调用的时候,会读取 LayoutParame 上的状态以决定是否调用该 Behavior 的 onNestedPreScroll 方法。另外,只有当一个 CoordinatorLayout 包含的所有的 Behavior 都不处理该滑动事件的时候,才判定 CoordinatorLayout 不处理该滑动事件。伪代码如下,在消费事件的时候是通过覆写 onNestedPreScroll() 等方法,以该方法为例,这里的 dx 和 dy 是滚动在水平和方向上的总的值,我们消费的值通过 consumed 指定。比如 dy 表示向上一共滚动了 50dp,而我们的 toolbar 需要先向上滚动 44dp,那么我们就将 44dp 的数值赋值给 consumed 数组(方法签名中的数组是按引用传递的)。这样父控件就可以将剩下的 6dp 交给列表,所以列表最终会向上滚动 6dp.按照上述 Behavior 的实现方式,一个 Behavior 是可以拦截到 CoordinatorLayout 内所有的 View 的 NestedScrolling 事件的。因而,我们可以在一个 Behavior 内部对 CoordinatorLayout 内的所有的 NestedScrolling 事件进行统筹拦截和调度。用一个图来表示整体分发逻辑,如下,这里需要注意,按照我们上面的分析,CoordinatorLayout 收集到的事件 NestedScrolling 事件,如果一个控件并没有实现 NestedScrollingChild 接口,或者更严谨得说,没有将滚动事件传递给 CoordinatorLayout,那么 Behavior 就无法接受到滚动事件。但是对于普通的触摸事件 Behavior 是可以拦截到的。关于“Behavior怎么实现复杂的视觉联动效果”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Behavior怎么实现复杂的视觉联动效果”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注百云主机行业资讯频道。

相关推荐: React组件API怎么使用

今天小编给大家分享一下React组件API怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。setState(object nextStat…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 07/09 17:23
下一篇 07/09 17:24

相关推荐