Android5.0提供了一个CoordinatorLayout布局,他能通过Behavior协调子布局之间的交互
自定义Behavior,MyBehavior继承自 CoordinatorLayout.Behavior,尖括号里面是需要此Behavior的View类,然后把MyBebavior写入String文件,然后再CoordinatorLayout子布局中引用。
下面贴出一个模拟支付宝财富页面的behavior:
public class ContentBehavior extends CoordinatorLayout.Behavior<ScrollView> {
private static final int INVALID_POINTER = -1;
private int mActivePointerId = INVALID_POINTER;
private int mTouchSlop;
private float mLastMotionX;
private float mLastMotionY;
private float mDownMotionY;
private boolean mIsBeingDragged = false;
private float mAutoTranslateY;
private LinearLayout bottomLL;
private ScrollView scrollView;
private float mscY;//scrollview主动要滑动的距离
public ContentBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
final ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull ScrollView child, @NonNull MotionEvent ev) {
if (scrollView == null) {
scrollView = child;
bottomLL = child.findViewById(R.id.bottom_ll);
}
final int action = ev.getAction();
if (action == MotionEvent.ACTION_CANCEL
|| action == MotionEvent.ACTION_UP) {
endDrag();
return false;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
int index = ev.getActionIndex();
mActivePointerId = ev.getPointerId(index);
if (mActivePointerId == INVALID_POINTER)
break;
mLastMotionX = ev.getX(index);
mLastMotionY = ev.getY(index);
mDownMotionY = ev.getY(index);
mIsBeingDragged = false;
break;
case MotionEvent.ACTION_MOVE:
if (mActivePointerId == INVALID_POINTER) {
break;
}
determineDrag(ev);
break;
}
return mIsBeingDragged;
}
private int getPointerIndex(MotionEvent ev, int id) {
int activePointerIndex = ev.findPointerIndex(id);
if (activePointerIndex == -1)
mActivePointerId = INVALID_POINTER;
return activePointerIndex;
}
@Override
public boolean onTouchEvent(@NonNull CoordinatorLayout parent, @NonNull ScrollView child, @NonNull MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
int index = ev.getActionIndex();
mActivePointerId = ev.getPointerId(index);
mLastMotionX = ev.getX();
mLastMotionY = ev.getY();
mDownMotionY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
determineDrag(ev);
}
if (mIsBeingDragged) {
final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
if (mActivePointerId == INVALID_POINTER)
break;
final float y = ev.getY(activePointerIndex);
float deltaY = y - mLastMotionY;
float translateY = deltaY;
if (deltaY > 0) {//向下运动
if (mscY + deltaY < 0) {
mscY = mscY + deltaY;
if (mscY > 0) {//如果当前滑动距离加上手指移动距离大于0,说明scrollview已经全部滑动完,child需要移动一部分
translateY = mscY;
child.scrollTo(0, 0);
} else {
translateY = 0;
child.scrollTo(0, Float.valueOf(-mscY).intValue());
}
}
if (bottomLL.getTranslationY() + deltaY > FarmProfitsViewHolder.totalAssetLayoutHeight) {
translateY = FarmProfitsViewHolder.totalAssetLayoutHeight - bottomLL.getTranslationY();
}
} else {
if (bottomLL.getTranslationY() + deltaY < 0) {
translateY = 0 - bottomLL.getTranslationY();
mscY = mscY + (deltaY - translateY);
child.scrollTo(0, Float.valueOf(-mscY).intValue());
}
}
TranslateEvent translateEvent = new TranslateEvent();
translateEvent.setTranslateY(bottomLL.getTranslationY() + translateY);
EventBus.getDefault().post(translateEvent);
bottomLL.setTranslationY(bottomLL.getTranslationY() + translateY);//滑动的距离
mLastMotionY = y;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged) {
final int activePointerIndex = getPointerIndex(ev, mActivePointerId);
if (mActivePointerId != INVALID_POINTER) {
final float y = ev.getY(activePointerIndex);
final int totalDelta = (int) (y - mDownMotionY);
if (bottomLL.getTranslationY() > 0) {//当移动距离大于0时候才会有自动滑动
if (totalDelta > 0) {
if (bottomLL.getTranslationY() > FarmProfitsViewHolder.totalAssetLayoutHeight / 4) {
mAutoTranslateY = FarmProfitsViewHolder.totalAssetLayoutHeight;
}
} else {
if (bottomLL.getTranslationY() < 3 * FarmProfitsViewHolder.totalAssetLayoutHeight / 4) {
mAutoTranslateY = 0;
}
}
TranslateEvent translateEvent = new TranslateEvent();
translateEvent.setTranslateY(mAutoTranslateY);
EventBus.getDefault().post(translateEvent);
bottomLL.animate().translationY(mAutoTranslateY).setDuration(100).start();
}
}
mActivePointerId = INVALID_POINTER;
mscY = 0;
endDrag();
}
break;
case MotionEvent.ACTION_POINTER_DOWN: {
final int index2 = ev.getActionIndex();
mLastMotionX = ev.getX(index2);
mLastMotionY = ev.getY(index2);
mActivePointerId = ev.getPointerId(index2);
break;
}
case MotionEvent.ACTION_POINTER_UP:
int pointerIndex = getPointerIndex(ev, mActivePointerId);
if (mActivePointerId == INVALID_POINTER)
break;
mLastMotionX = ev.getX(pointerIndex);
mLastMotionY = ev.getY(pointerIndex);
break;
}
return true;
}
private void determineDrag(MotionEvent ev) {
final int activePointerId = mActivePointerId;
final int pointerIndex = getPointerIndex(ev, activePointerId);
if (activePointerId == INVALID_POINTER || pointerIndex == INVALID_POINTER)
return;
final float x = ev.getX(pointerIndex);
final float dx = x - mLastMotionX;
final float xDiff = Math.abs(dx);
final float y = ev.getY(pointerIndex);
final float dy = y - mLastMotionY;
final float yDiff = Math.abs(dy);
if (yDiff > mTouchSlop && yDiff > xDiff) {//垂直滑动的距离大于最小滑动距离并且大于水平距离
mIsBeingDragged = true;
mLastMotionX = x;
mLastMotionY = y;
}
if (dy > 0) {//向下
if (bottomLL.getTranslationY() == FarmProfitsViewHolder.totalAssetLayoutHeight) {
mIsBeingDragged = false;
}
}
if (dy < 0) {//向上
if (bottomLL.getTranslationY() == 0) {
mIsBeingDragged = false;
}
}
if (scrollView.getScrollY() > 0) {
mIsBeingDragged = false;
}
}
private void endDrag() {
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
}