嵌套滑动机制主要涉及两个接口
代码语言:javascript复制public interface NestedScrollingChild {
void setNestedScrollingEnabled(boolean var1);
boolean isNestedScrollingEnabled();
boolean startNestedScroll(int var1);//开启嵌套滚动流程
//循环遍历parent,先判断是否是NestedScrollingParent的实例,如果是,那就调用parent的onStartNestedScroll和onNestedScrollAccepted
void stopNestedScroll();
boolean hasNestedScrollingParent();
boolean dispatchNestedScroll(int var1, int var2, int var3, int var4, @Nullable int[] var5);
boolean dispatchNestedPreScroll(int var1, int var2, @Nullable int[] var3, @Nullable int[] var4);
//在子view自己进行滚动之前调用此方法,询问父view是否要在子view之前进行滚动。
//此方法的前两个参数用于告诉父View此次要滚动的距离;而第三第四个参数用于子view获取父view消费掉的距离和父view位置的偏移量。
//第一第二个参数为输入参数,即常规的函数参数,调用函数的时候我们需要为其传递确切的值。而第三第四个参数为输出参数,调用函数时我们只需要传递容器(在这里就是两个数组),在调用结束后,我们就可以从容器中获取函数输出的值。
//如果parent消费了一部分或全部距离,则此方法返回true。
boolean dispatchNestedFling(float var1, float var2, boolean var3);
boolean dispatchNestedPreFling(float var1, float var2);
}
public interface NestedScrollingParent {
boolean onStartNestedScroll(@NonNull View var1, @NonNull View var2, int var3);//决定是否要配合其进行嵌套滚动
void onNestedScrollAccepted(@NonNull View var1, @NonNull View var2, int var3);
void onStopNestedScroll(@NonNull View var1);
void onNestedScroll(@NonNull View var1, int var2, int var3, int var4, int var5);
void onNestedPreScroll(@NonNull View var1, int var2, int var3, @NonNull int[] var4);
//child调用dispatchNestedPreScroll(),这时可以回调到parent的OnNestedPreScroll(),parent可以在这个回调中先于child滚动。
boolean onNestedFling(@NonNull View var1, float var2, float var3, boolean var4);
boolean onNestedPreFling(@NonNull View var1, float var2, float var3);
int getNestedScrollAxes();
}
对它们打log如下
代码语言:javascript复制E/child: startNestedScroll----- 是否开启
E/parent: onStartNestedScroll——是否配合
E/child: dispatchNestedPreScroll——如果为true,将事件给parent
E/parent: onNestedPreScroll——此时dy有数据而consume没数据,给consume数据赋值。赋完值马上回传
E/数据parent: dy=14,consumed[1]0
E/数据child: dy=14,consumed[1]14
所以一般嵌套滑动的child都有如下重要的函数
代码语言:javascript复制@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
//按下
case MotionEvent.ACTION_DOWN:
lastY = (int) event.getRawY();
startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL | ViewCompat.SCROLL_AXIS_VERTICAL);//开启嵌套滑动
break;
//移动
case MotionEvent.ACTION_MOVE:
......
if (dispatchNestedPreScroll(0, dy, consumed, offset)) {//如果找到了支持嵌套滑动的父类,父类进行了一系列的滑动
Log.e("数据child", "dy=" dy ",consumed[1]" consumed[1]);
//获取滑动距离进行滑动,具体情况具体分析
int remain = dy - consumed[1];
if (remain != 0) {
scrollBy(0, -remain);
}
} else {//如果没有嵌套滑动,那么自己滑动
scrollBy(0, -dy);
}
break;
case MotionEvent.ACTION_UP:
stopNestedScroll();
break;
}
return true;
}
父类实例中最重要的函数
代码语言:javascript复制//在此可以判断参数target是哪一个子view以及滚动的方向,然后决定是否要配合其进行嵌套滚动
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
Log.e("parent", "onStartNestedScroll");
if (target instanceof MyNestedScrollingChild2) {
return true;
}
return false;
}
//先于child滚动
//前3个为输入参数,最后一个是输出参数
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
Log.e("parent", "onNestedPreScroll");
Log.e("数据parent", "dy=" dy ",consumed[1]" consumed[1]);
if (showImg(dy) || hideImg(dy)) {//如果需要显示或隐藏图片,即需要自己(parent)滚动
scrollBy(0, -dy);//滚动
consumed[1] = dy;//告诉child我消费了多少
}
}
参考文章:https://www.cnblogs.com/wjtaigwh/p/6398562.html
但是
一般情况下不大可能就是一个光秃秃的NestedScrollingChild。如果child是recycleview的情况呢(里面还有滑动事件,而且本身还继承NestedScrollingChild) 我们先来看看recycleview的onTouchEvent函数
代码语言:javascript复制switch (action) {
case MotionEvent.ACTION_DOWN: {
mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
mInitialTouchX = mLastTouchX = (int) (e.getX() 0.5f);
mInitialTouchY = mLastTouchY = (int) (e.getY() 0.5f);
int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
if (canScrollHorizontally) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
}
if (canScrollVertically) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
}
startNestedScroll(nestedScrollAxis);//******************************************************************
} break;
case MotionEventCompat.ACTION_POINTER_DOWN: {
mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) 0.5f);
mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) 0.5f);
} break;
case MotionEvent.ACTION_MOVE: {
final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
if (index < 0) {
Log.e(TAG, "Error processing scroll; pointer index for id "
mScrollPointerId " not found. Did any MotionEvents get skipped?");
return false;
}
final int x = (int) (MotionEventCompat.getX(e, index) 0.5f);
final int y = (int) (MotionEventCompat.getY(e, index) 0.5f);
int dx = mLastTouchX - x;
int dy = mLastTouchY - y;
if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {//*************************************************
dx -= mScrollConsumed[0];
dy -= mScrollConsumed[1];
vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
// Updated the nested offsets
mNestedOffsets[0] = mScrollOffset[0];
mNestedOffsets[1] = mScrollOffset[1];
}
if (mScrollState != SCROLL_STATE_DRAGGING) {
boolean startScroll = false;
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
if (dx > 0) {
dx -= mTouchSlop;
} else {
dx = mTouchSlop;
}
startScroll = true;
}
if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
if (dy > 0) {
dy -= mTouchSlop;
} else {
dy = mTouchSlop;
}
startScroll = true;
}
if (startScroll) {
setScrollState(SCROLL_STATE_DRAGGING);
}
}
if (mScrollState == SCROLL_STATE_DRAGGING) {//*****************************在这里随着手指的移动而移动,调用scrollByInternal
mLastTouchX = x - mScrollOffset[0];
mLastTouchY = y - mScrollOffset[1];
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
vtev)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
} break;
具体parent代码也可以参考下面的文章(之所以加dispatchTouchEvent是为了增加快速滑动效果,否则滑动效果太生硬)
参考文章:http://blog.csdn.net/lmj121212/article/details/53046582