CoordinatorLayout+ViewPager+SwipeRefreshLayout滑動事件衝突的處理
阿新 • • 發佈:2019-01-26
只是一個搬運工
連結
參考
程式碼(自己加的註釋,好多不懂,勿噴)
public class NestedScrollSwipeRefreshLayout extends SwipeRefreshLayout implements NestedScrollingChild {
private static final int INVALID_POINTER = -1;
//最小有效滑動
private int mTouchSlop;
private int mActivePointerId = INVALID_POINTER;
private NestedScrollingChildHelper mChildHelper;
public NestedScrollSwipeRefreshLayout(Context context) {
this(context, null);
}
public NestedScrollSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mChildHelper = new NestedScrollingChildHelper(this );
setNestedScrollingEnabled(true);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public void setNestedScrollingEnabled(boolean enabled) {
mChildHelper.setNestedScrollingEnabled(enabled);
}
@Override
public boolean isNestedScrollingEnabled () {
return mChildHelper.isNestedScrollingEnabled();
}
/**
* 實現一些跟NestedScrollingParent互動的一些方法,通知父view
* @param axes
* @return
*/
@Override
public boolean startNestedScroll(int axes) {
return mChildHelper.startNestedScroll(axes);
}
/**
* 結束整個流程。
*/
@Override
public void stopNestedScroll() {
mChildHelper.stopNestedScroll();
}
@Override
public boolean hasNestedScrollingParent() {
return mChildHelper.hasNestedScrollingParent();
}
/**
* 向父view彙報滾動情況,包括子view消費的部分和子view沒有消費的部分。
* 這個函式一般在子view處理scroll後呼叫。
* @param dxConsumed
* @param dyConsumed
* @param dxUnconsumed
* @param dyUnconsumed
* @param offsetInWindow
* @return 如果父view接受了它的滾動引數,進行了部分消費,則這個函式返回true,否則為false。
*/
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
}
/**
* 一般在MotionEvent.Move裡呼叫 通知父View滑動的距離
*
*
* 由於窗體進行了移動,如果你記錄了手指最後的位置,需要根據第四個引數offsetInWindow計算偏移量,
* 才能保證下一次的touch事件的計算是正確的。
* 一般在子view處理scroll前呼叫
* @param dx
* @param dy
* @param consumed 第一個元素是父view消費的x方向的滾動距離;第二個元素是父view消費的y方向的滾動距離
* @param offsetInWindow 子View的窗體偏移量
* @return 如果父view接受了它的滾動引數,進行了部分消費,則這個函式返回true,否則為false
*/
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
@Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}
@Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
//記住最後按下時點的y值
private float mLastMotionY;
private final int[] mScrollOffset = new int[2];
private final int[] mScrollConsumed = new int[2];
//標識是否已經開始拖動
private boolean mIsBeginDrag = false;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN: {
//獲得觸控點id.up的時候id會失效
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
final float initialDownY = getMotionEventY(ev, mActivePointerId);
//為-1,多點觸控,之後擡起,把事件交給父佈局處理
if (initialDownY == -1) {
return false;
}
//儲存按下的y值,填充給最後移動的距離
mLastMotionY = initialDownY;
//通知父view開始巢狀滾動
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
super.onInterceptTouchEvent(ev);
//初始化標識
mIsBeginDrag = false;
break;
}
case MotionEvent.ACTION_MOVE: {
//如果觸控點id 為 -1 把點選事件交給父佈局處理
if (mActivePointerId == INVALID_POINTER) {
return false;
}
//標記觸控點的y值
final float y = getMotionEventY(ev, mActivePointerId);
//為-1,多點觸控,之後擡起,把點選事件交給父佈局處理
if (y == -1) {
return false;
}
//計算移動的距離
int deltaY = (int)(mLastMotionY - y);
//更新 最後按下時點的y值
mLastMotionY = y;
//移動的距離是否有效,更新 是否正在拖動的標識
if (Math.abs(deltaY) >= mTouchSlop) {
mIsBeginDrag = true;
}
/**
* 正在拖動,父view消耗了部分view,就交給父view處理
* mScrollOffset[1] 子view視窗y軸的偏移量
* mScrollOffset[1] 父view消費的y軸偏移量
*/
if (mIsBeginDrag && dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
mLastMotionY -= mScrollOffset[1];//修正最後按下的點的y值
deltaY -= mScrollConsumed[1];//修正被父view消耗後的移動的距離
ev.offsetLocation(0, mScrollConsumed[1]);//修正ev的位置
//向父View彙報,如果父view接受了引數,進行了部分消費,返回true,否則返回false
if (dispatchNestedScroll(0, 0, 0, deltaY, mScrollOffset)) {
mLastMotionY -= mScrollOffset[1];//修正最後按下的點的y值
ev.offsetLocation(0, mScrollOffset[1]);//修正點選的位置
}
return false;
} else {
//否則正常處理
return super.onInterceptTouchEvent(ev);
}
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
//停止巢狀滑動
stopNestedScroll();
//初始化相關值
mActivePointerId = INVALID_POINTER;
mIsBeginDrag = false;
return super.onInterceptTouchEvent(ev);
}
}
return super.onInterceptTouchEvent(ev);
}
/**
* 返回 座標的y值
* 解決多點觸控的問題
* @param ev
* @param activePointerId
* @return
*/
private float getMotionEventY(MotionEvent ev, int activePointerId) {
//得到觸點的索引值,範圍是 0 到 ev.getPointerCount()-1;
final int index = MotionEventCompat.findPointerIndex(ev, activePointerId);
if (index < 0) {
return -1;
}
return MotionEventCompat.getY(ev, index);
}
}