重要的ui组件——Behavior
v7包下的组件类似CoordinatorLayout推出也有一段时间了,大家使用的时候应该会体会到其中很多的便利,今天这篇文章带大家来了解一个比较重要的ui组件——Behavior。从字面意思上就可以看出它的作用,就是用来规定某些组件的行为的,那它到底是什么,又该怎么用呢?看完这篇文章希望大家会有自己的收获~
前言
写这篇文章的起因是因为我无意中在GitHub上发现了Jake Wharton大神新建了一个Repo,内容是JakeWharton/DrawerBehavior。有兴趣的同学可以去看看,其实就是通过Behavior去构造一个类似于DrawerLayout的布局。想了想已经挺长时间没有搞ui方面的代码了,所以趁着这个机会复习了一下,顺便写一篇文章巩固,也给想要了解这方面内容的同学一个平台吧。
Behavior是什么
在文章的开始,我们先要了解什么是Behavior。
1 |
/** |
它是CoordinatorLayout的内部类,从它的注释和其中的方法可以看出来,它其实就是给CoordinatorLayout的子View提供了一些交互的方法,用来规范它们的交互行为,比如上面出现的onTouchEvent可以用来规范子View的触摸事件,onLayoutChild可以用来规范子View的布局。
说到这里,大家可能会有一个问题,CoordinatorLayout又是个什么东西?
1 |
public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent { |
可以看出,它其实就是一个ViewGroup,实现了NestedScrollingParent用来执行嵌套滑动。至于嵌套滑动的机制大家可以看我博客的第一篇文章,这不是我们这篇文章的重点。
既然CoordinatorLayout仅仅只是一个ViewGroup,它又为什么能展示出它在xml布局中展示的威力呢?其中的秘密就是在Behavior中。我们可以这么说,CoordinatorLayout利用了Behavior作为一个代理,去控制管理其下的子View做到各种布局和动画效果。那为什么要使用Behavior呢?我想原因大概就是解耦吧,如果把所有的逻辑都写死在CoordinatorLayout中,一来不利于维护,二来我们就没有做一些自定义的事情,会显得非常的笨重。
为什么要用Behavior
这里我们举一个非常简单的例子。首先来看看我们的布局文件。
1 |
<?xml version="1.0" encoding="utf-8"?> |
非常简单有木有,CoordinatorLayout作为根布局,里面一个AppBarLayout一个RecyclerView。让我们看看界面是怎么样的。
可以看到显示是正确的。但是如果我把xml里RecyclerView的那行layout_behavior删掉呢?就像这样。
1 |
<?xml version="1.0" encoding="utf-8"?> |
最终界面的展示就像这样,RecyclerView把AppBarLayout给覆盖了。这里其实很好理解,如刚才的代码所示,CoordinatorLayout其实只是一个ViewGroup,它不像LinearLayout那样具有特定的布局特点,甚至可以说它内部的逻辑和FrameLayout是没什么差别的,所以如果你不设置对应的Behavior的话,布局就会有问题。从这里也可以反映出Behavior的作用,就是规范子View的显示和交互。
原理&系统是怎么用Behavior的
说完了Behavior的作用,那该怎么用它呢?这一小节让我们来讲讲Behavior的原理以及系统是如何使用它的。
首先先看原理。我们知道Behavior是用来帮助CoordinatorLayout的,所以我们要从CoordinatorLayout中寻找答案。首先,我们可以看到CoordinatorLayout中有一个LayoutParams,它的子View的LayoutParams都是这个,其中它的构造函数如下。
1 |
LayoutParams(Context context, AttributeSet attrs) { |
可以看到它通过parseBehavior去得到了对应子View的Behavior。大家可以试试用RecyclerView的getLayoutParams方法去获取LayoutParams并且调用getBehavior方法,可以得到的就是我们在xml文件中设置的那个Behavior。
知道了如何将Behavior设置进去,那它是如何发挥作用的呢?让我们来看看onLayout函数。
1 |
@Override |
可以看到的是其中会先调用behavior.onLayoutChild(this, child, layoutDirection)。也就是说,Behavior的逻辑要优先于CoordinatorLayout自己的逻辑。其实不止是onLayout,我们还可以看看onTouchEvent这个函数。
1 |
public boolean onTouchEvent(MotionEvent ev) { |
可以看到也是调用了Behavior的onTouchEvent,我们可以下判断说Behavior中的那些方法在CoordinatorLayout中都会在合适的时机去调用。这也证明了我们刚才的那句话:[Behavior就是CoordinatorLayout的代理,帮助它去管理子View]。
我们做一个总结,Behavior可以代理哪些行为呢?
1.Measure和Layout的布局行为。
2.onTouchEvent和onInterceptTouchEvent的触摸行为。比如design包中的SwipeDismissBehavior就是通过这样的方式完成的。
3.嵌套滑动行为(NestedScrollingParent和NestedScrollingChild中的逻辑)。
4.子View间的依赖行为。
对于第四点我们这里可以细说一下,什么叫子View的依赖行为呢?这里我们举个例子,我们都知道如果在CoordinatorLayout中使用了FAB并且点击展示SnackbarLayout的话,FAB会在Snackbar显示的时候对应的上移,这是因为FAB依赖了SnackbarLayout。
1 |
public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> { ........ @Override |
这是FAB中的Behavior,可以看到它重写了layoutDependsOn和onDependentViewChanged,里面的逻辑很简单的就可以看明白。这里我们[将代码翻译成语言]就是说FAB要依赖的组件是SnackbarLayout,所以在之后的操作里当DependentView(SnackbarLayout)发生了改变,自己(FAB)也会相应的做出改变。
值得一提的是,onDependentViewChanged这个函数的调用时机并不是在onLayout之前,而是在onPreDraw中,具体代码如下:
1 |
class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener { |
如此简单的处理View间的依赖,可见Behavior配合CoordinatorLayout是有多强大。下面我们可以再举一个例子来讲讲Behavior的作用。还记得我们上面说的吗?RecyclerView设置了一个Behavior它就可以和AppBarLayout很好的展示出来。这个Behavior的名字是:
1 |
app:layout_behavior="@string/appbar_scrolling_view_behavior" <string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string> |
可以看到它是AppBarLayout里的一个内部类,让我们看看它做了什么。
1 |
@Override |
我们知道,如果不设置这个Behavior的话,RecyclerView会覆盖AppBarLayout。而上面这段代码里的逻辑就可以很好的解释这个原因了。值得一提的是,在offsetChildAsNeeded方法中有这么一段:
1 |
final CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); |
这里dependency就是AppBarLayout,所以我们可以知道,AppBarLayout中有两个Behavior,一个是我们前面提到的ScrollingViewBehavior,用来处理它和其他滑动View的关系,另外一个就是Behavior,用来处理自己的逻辑,比如Layout。通过这种巧妙的方式,我们就可以做到非常简便的控制View本身和View之间的逻辑。
如何自定义Behavior
本来想写个demo给大家看一看的,不过感觉还是不要重复造轮子了,还是没用的轮子。推荐大家看SwipeDismissBehavior用法及实现原理这篇文章和一开始提到的Jake大神的新作DrawerBehavior。如果你把这两个东西搞懂,那么Behavior你可以说已经完全没问题了~
后记
最近一段时间都在搞hotPatch和插件化相关的东西,看了很多Framework层的源码,要做的东西也做的七七八八,希望快点解决最后的几个bug并且之后能开源和大家见面吧
重要的ui组件——Behavior的更多相关文章
- iOS之UI组件整理
作者:神兽gcc 授权本站转载. 最近把iOS里的UI组件重新整理了一遍,简单来看一下常用的组件以及它们的实现.其实现在这些组件都可以通过Storyboard很快的生成,只是要向这些组件能够变得生动起 ...
- Atitit.web ui 组件化 vs mvc
Atitit.web ui 组件化 vs mvc 组件化 与 mvc并不矛盾..单双方适用的地方有所不同.. React推荐以组件的方式去重新思考UI构成,将UI上每一个功能相对独立的模块定 ...
- 这是一个比较全的Android UI 组件
Android组件及UI框架大全 原文地址:http://blog.csdn.net/smallnest/article/details/38658593 Android 是目前最流行的移动操作系统 ...
- AngularJs的UI组件ui-Bootstrap分享(一)
最近几个月学习了AngularJs和扩展的UI组件,并在公司小组内做了一次分享交流,感觉很有收获,在此记录下个人的学习心得. 目录: AngularJs的UI组件ui-Bootstrap分享(一) A ...
- AngularJs的UI组件ui-Bootstrap分享(十四)——Carousel
Carousel指令是用于图片轮播的控件,引入ngTouch模块后可以在移动端使用滑动的方式使用轮播控件. <!DOCTYPE html> <html ng-app="ui ...
- AngularJs的UI组件ui-Bootstrap分享(十三)——Progressbar
进度条控件有两种指令,第一种是uib-progressbar指令,表示单一颜色和进度的一个进度条.第二种是uib-bar和uib-progress指令,表示多种颜色和多个进度组合而成的一个进度条. 这 ...
- AngularJs的UI组件ui-Bootstrap分享(十二)——Rating
Rating是一个用于打分或排名的控件.看一个最简单的例子: <!DOCTYPE html> <html ng-app="ui.bootstrap.demo" x ...
- AngularJs的UI组件ui-Bootstrap分享(十一)——Typeahead
Typeahead指令是一个用于智能提示或自动完成的控件,就像Jquery的AutoComplete控件一样.来看一个最简单的例子: <!DOCTYPE html> <html ng ...
- AngularJs的UI组件ui-Bootstrap分享(十)——Model
Model是用来创建模态窗口的,但是实际上,并没有Model指令,而只有$uibModal服务,创建模态窗口是使用$uibModal.open()方法. 创建模态窗口时,要有一个模态窗口的模板和对应的 ...
随机推荐
- 【python基础】--常用数据结构
list tuple dict set四种常用数据结构 list list 有序的集合,可以随时添加.删除其中元素值; 支持list嵌套模式, >>> p = ['a','b']&g ...
- Android Service完全解析
Service的基本用法 1.新建一个Android项目,新建一个MyService继承自Service,并重写父类的onCreate(),onStartCommand()方法和onDestory() ...
- 201621123033 《Java程序设计》第8周学习总结
第八次作业 1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 1. ArrayList代码分析 1.1 解释ArrayList的contains源代码 首先调 ...
- Apache实现一个ip(如:127.0.0.1)和多个域名(虚拟主机)绑定
今天在学习PHP时,有这样的一个需求:一个ip(如:127.0.0.1)和多个域名(虚拟主机)绑定,以下是我的解决方案:对Apache进行相关的配置 解决方案一:通过端口来区分不同的虚拟主机 ①按照绑 ...
- sqlachemy 原生sql输出
在创建引擎时,将echo参数配置成True,会输出sql执行语句记录.默认False create_engine(statsticConf.sqlalchemy_mysql,connect_args= ...
- nginx教程全集汇总
Nginx基础1. nginx安装:httpwww.ttlsa.comnginxnginx-install-on-linux2. nginx 编译参数详解(运维不得不看):http://www.t ...
- Java 命令行编译项目
如果是用Exlipse, 第三方的包可以放在eclipse文件夹的jre包的lib文件夹中! (初学者的一些总结-高手们勿喷哈-) 原因: 以前一直用Eclispe编程环境运行Java.非常舒服,就像 ...
- HDU 1087 最大上升子序列的和
Super Jumping! Jumping! Jumping! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 ...
- ACdream 1025 bfs
Transform Time Limit: 4000/2000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) Submit St ...
- async-http
android-async-http开源框架可以是我们轻松的获取网络数据或者向服务器发送数据,使用起来也很简单,下面做简单介绍,具体详细使用看官网:https://github.com/loopj/a ...