说起CoordinatorLayout,一直不是很明了,也没正在用过,只是看到现在项目中有使用到。就跟着《Android进阶之光》以及一篇博客CoordinatorLayout的使用如此简单学习了。
CoordinatorLayout是协调者布局,协调子view的。
通常用法是和Appbarlayout、CollapsingToolBarLayout结合使用。但是要理解深刻,必须先分开讲。
先看一个图:
这个效果是 拖动蓝色方块,旁边的按钮会随之滑动:Y方向相同,X方向相反。
以前实现思路可能就是,在自定义蓝色View重写onTouchEvent,持有button的引用,根据手指活动的位置对button进行布局。如果把button换成其他view又要持有其他view的引用,耦合度搞。 现在看下如何使用CoordinatorLayout来协调蓝色View和button来实现这个效果。
CoordinatorLayout的使用核心是Behavior
Behavior就是你定制的行为,例如上面的button随着蓝色view的移动,即 button 依赖 蓝色view 而表现出在Behavior中定制的行为。
怎么定义Behavior呢?
1、继承CoordinatorLayout.Behavior,实现layoutDependsOn、onDependentViewChanged两个方法。相关说明在注释中有写到。
public class TestBehavior extends CoordinatorLayout.Behavior<Button> {
/**
* 屏幕宽度
*/
private int mWidth;
public TestBehavior() {
}
public TestBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
mWidth = displayMetrics.widthPixels;
}
/**
* 判断child的布局是否依赖dependency
* @param parent CoordinatorLayout
* @param child 执行动作的CoordinatorLayout的子View,即设置这个behavior的view
* @param dependency Child依赖的View,需要关心的view
* @return 返回false表示child不依赖dependency,true表示依赖
*/
@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull Button child, @NonNull View dependency) {
//如果dependency是MovingView的实例,说明它就是我们所需要的Dependency
return dependency instanceof MovingView;
}
/**
* 当dependency发生改变时(位置、宽高等),执行这个函数
* @param parent
* @param child
* @param dependency
* @return 返回true表示 child的位置或者是宽高 要发生改变,否则就返回false
*/
@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull Button child, @NonNull View dependency) {
int left = dependency.getLeft();
int top = dependency.getTop();
//dependency移动时,child随着dependency移动:竖直方向一致,水平方向反向移动
child.layout(mWidth - left - child.getWidth(), top, mWidth - left, top + child.getHeight());
return true;
}
}
2、为Button设置Behavior
<?xml version="1.0" encoding="utf-8"?><!--使用CoordinatorLayout-->
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".module.home.BehaviorTestActivity">
<!--随手指滑动的view-->
<com.hfy.demo01.common.customview.MovingView
android:id="@+id/mv_test"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@color/colorPrimary" />
<!--给Button设置定义好的layout_behavior-->
<!--CoordinatorLayout的使用核心是Behavior,Behavior就是执行你定制的动作。-->
<Button
android:id="@+id/btn_test_behavior"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test_behavior"
android:textAllCaps="false"
app:layout_behavior="com.hfy.demo01.module.home.TestBehavior" />
</android.support.design.widget.CoordinatorLayout>
以上。
附上滑动view的代码:
public class MovingView extends View {
/**
* 手指按下的初始X坐标
*/
private int mLastX;
/**
* 手指按下的初始Y坐标
*/
private int mLastY;
public MovingView(Context context) {
super(context);
}
public MovingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MovingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
//触摸点相对view的坐标。(滑动过程中,这个值不变)
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_MOVE:
//滑动的距离 = 触摸点滑动到的坐标 - 开始触摸的坐标 (都是相对于view本身)
int offsetX = x - mLastX;
int offsetY = y - mLastY;
//所以View也要跟上这个滑动距离
layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
break;
case MotionEvent.ACTION_UP:
// Toast.makeText(getContext(), "点了", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
return true;
}
}