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 is intended for two primary use cases:
上面是官方给的解释,CoordinatorLayout的作用就两个:
所以这里最重要就是这个Behavior了,如果你完全掌握了Behavior,CoordinatorLayout就搞定了
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类中的以下方法:
@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;
//每次dependency位置发生变化,都会执行onDependentViewChanged方法
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
return false;
}
这个方法,可以在这个回调中记录dependency的一些位置信息,在onLayoutChild中利用保存下来的信息进行计算,然后得到自身的具体位置。
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
return false;
}
这个方法是用来子view用来布局自身使用,如果依赖其他view,那么系统会首先调用
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的大小来决定自身的大小。
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机制完全解析 带你玩转嵌套滑动
实现如下红色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 初探