Android(安卓)Material Design之CoordinatorLayout
一、CoordinatorLayout简介
java.lang.Object ↳ android.view.View ↳ android.view.ViewGroup ↳ android.support.design.widget.CoordinatorLayout
实现了NestedScrollingParent接口,CoordinatorLayout, NestedScrollView, SwipeRefreshLayout都实现了这个接口
CoordinatorLayout is a super-powered FrameLayout. 所以属性和FrameLayout有点像
CoordinatorLayout 控件是design下最重要的一个控件,也是最复杂、功能最强大的,这从他的作用就可以看的出来
二、CoordinatorLayout作用
CoordinatorLayout is intended for two primary use cases:
- As a top-level application decor or chrome layout
- As a container for a specific interaction with one or more child views
上面是官方给的解释,CoordinatorLayout的作用就两个:
- 作为一个最顶层根布局因为CoordinatorLayout本身就是继承ViewGroup
- 作为一个容器协调子View的行为(通过CoordinatorLayout.Behavior)
所以这里最重要就是这个Behavior了,如果你完全掌握了Behavior,CoordinatorLayout就搞定了
三、CoordinatorLayout.Behavior研究
public static abstract class Behavior<V extends View>
查看CoordinatorLayout的源码我们可以看到这是一个抽象类,里面定义了很多方法,如果需要使用我们要继承CoordinatorLayout.Behavior然后重新一些方法。
我们拿AppBarLayout为例,AppBarLayout中有两个Behavior,一个是拿来给它自己用的,另一个是拿来给它的兄弟结点用的,我们重点关注下AppBarLayout.ScrollingViewBehavior这个类。
我们用到的appbar_scrolling_view_behavior指的也是AppBarLayout.ScrollingViewBehavior这个类
app:layout_behavior="@string/appbar_scrolling_view_behavior"
通过查看ScrollingViewBehavior也是继承CoordinatorLayout.Behavior
java.lang.Object ↳ android.support.design.widget.CoordinatorLayout.Behavior.view.View> ↳ android.support.design.widget.AppBarLayout.ScrollingViewBehavior
我们看看CoordinatorLayout.Behavior类中的以下方法:
1、layoutDependsOn
@Override public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { //如果dependency是AppBarLayout的实例,说明它就是我们所需要的Dependency return dependency instanceof AppBarLayout; }
这个方法告诉CoordinatorLayout,这个view是依赖AppBarLayout的,后续父亲可以利用这个方法,查找到这个child所有依赖的兄弟结点。在CoordinatorLayout.Behavior直接return false;
2、onDependentViewChanged
//每次dependency位置发生变化,都会执行onDependentViewChanged方法 @Override public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { return false; }
这个方法,可以在这个回调中记录dependency的一些位置信息,在onLayoutChild中利用保存下来的信息进行计算,然后得到自身的具体位置。
3、onLayoutChild
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) { return false; }
这个方法是用来子view用来布局自身使用,如果依赖其他view,那么系统会首先调用
4、onMeasureChild
public boolean onMeasureChild(CoordinatorLayout parent, V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { return false; }
这个是CoordinatorLayout在进行measure的过程中,利用Behavior对象对子view进行大小测量的一个方法。
在这个方法内,我们可以通过parent.getDependencies(child);这个方法,获取到这个child依赖的view,然后通过获取这个child依赖的view的大小来决定自身的大小。
5、NestedScroll
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { return false; } public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { // Do nothing } public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { // Do nothing } public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { // Do nothing }
这几个方法刚好是NestedScrollingParent的方法(CoodinatorLayout是继承了NestedScrollingParent的),也就是对CoodinatorLayout进行的一个代理(Proxy),即CoordinatorLayout自己不对这些消息进行处理,而是传递给子view的Behavior,进行处理。利用这样的方法,实现了view和view之间的交互和视觉的协同(布局、滑动)。
有兴趣可以看 鸿 洋大神的NestedScrolling机制完全解析:
Android NestedScrolling机制完全解析 带你玩转嵌套滑动
四、自定义Behavior
实现如下红色button,高度保持跟蓝色View一样,x轴方向相反
构造方法
public MyBehavior(Context context, AttributeSet attrs) { super(context, attrs); DisplayMetrics display = context.getResources().getDisplayMetrics(); width = display.widthPixels; }
一定要重写这个构造函数。因为CoordinatorLayout源码中parseBehavior()函数中直接反射调用这个构造函数。
static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] { Context.class, AttributeSet.class};
然后我们实现layoutDependsOn、onDependentViewChanged方法,自定义Behavior就完成了
下面是MyBehavior 的源码
public class MyBehavior extends CoordinatorLayout.Behavior<Button> { private int width; public MyBehavior(Context context, AttributeSet attrs) { super(context, attrs); DisplayMetrics display = context.getResources().getDisplayMetrics(); width = display.widthPixels; } @Override public boolean layoutDependsOn(CoordinatorLayout parent, Button child, View dependency) { //如果dependency是TempView的实例,说明它就是我们所需要的Dependency return dependency instanceof FollowView; } //每次dependency位置发生变化,都会执行onDependentViewChanged方法 @Override public boolean onDependentViewChanged(CoordinatorLayout parent, Button child, View dependency) { //根据dependency的位置,设置Button的位置 int top = dependency.getTop(); int left = dependency.getLeft(); int x = width - left - child.getWidth(); int y = top; setPosition(child, x, y); return true; } private void setPosition(View v, int x, int y) { CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) v.getLayoutParams(); layoutParams.leftMargin = x; layoutParams.topMargin = y; v.setLayoutParams(layoutParams); }}
项目源码
https://github.com/Yi520153/DesignCoordinatorLayout
总结
可以看到CoodinatorLayout给我们实现了一个可以被子view代理实现方法的一个布局。这和传统的ViewGroup不同,子view从此知道了彼此之间的存在,一个子view的变化可以通知到另一个子view。CoordinatorLayout所做的事情就是当成一个通信的桥梁,连接不同的view。使用Behavior对象进行通信。
参考资料
CoordinatorLayout的使用如此简单
关于CoordinatorLayout与Behavior的一点分析
CoordinatorLayout布局的使用方式
CoordinatorLayout与滚动的处理
Android Support Design 中 CoordinatorLayout 与 Behaviors 初探
更多相关文章
- 全屏
- Android(安卓)打开浏览器的几种方法
- android onTouch()与onTouchEvent()的区别
- Android(安卓)JNI介绍
- Android中hardware源码(android-5.0.2)
- android 源码
- Android精选完整源码之控件指示器视频压缩日历源码!
- 详解 Android(安卓)的 Activity 组件
- Android进程与线程基本知识