前言
本篇同上一篇實現同樣的功能,此篇採用之前音樂鎖屏壁紙的功能裏面使用的ViewDragHelper來實現,最終的效果與上篇效果相同,因此本篇只挑新內容講解。
具體步驟
事件分發與衝突解決
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean canScrollHorizontally = canScrollHorizontally(-1, this);
if (!canScrollHorizontally) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
說明:上一篇已經說清一個事件裏面包含三個小事件,因爲此篇主要是把事件交由ViewDragHelper處理,因此處理canScrollHorizontally()方法時,判斷條件不能只放在Move事件中處理,否則Down、Up的事件ViewDragHelper接收不到,查看源碼知道mViewDragHelper.shouldInterceptTouchEvent(ev)和mViewDragHelper.processTouchEvent(event)方法必須要三個事件同時存在。
滑動處理
通過ViewDragHelper實現滑動效果,主要是通過實現ViewDragHelper的回調ViewDragHelper.Callback實現滑動效果。
@Override
public boolean tryCaptureView(View child, int pointerId) {
return false;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//當前回調,鬆開手時觸發,比較觸發條件和當前的滑動距離
int left = releasedChild.getLeft();
if (left <= mMaxSlideWidth) {
//緩慢滑動的方法,小於觸發條件,滾回去
mViewDragHelper.settleCapturedViewAt(0, 0);
} else {
//大於觸發條件,滾出去...
mViewDragHelper.settleCapturedViewAt(mScreenWidth, 0);
}
//需要手動調用更新界面的方法
invalidate();
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
mScrollPercent = Math.abs((float) left
/ (mScreenWidth + mEdgeShadow.getIntrinsicWidth()));
//重繪
invalidate();
//當滾動位置到達屏幕最右邊,則關掉Activity
if (changedView == mRootView && left >= mScreenWidth) {
mActivity.finish();
}
}
@Override
public int getViewHorizontalDragRange(View child) {
return ViewDragHelper.EDGE_LEFT;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//限制左右拖拽的位移
left = left >= 0 ? left : 0;
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//上下不能移動,返回0
return 0;
}
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
//觸發邊緣時,主動捕捉mRootView
mViewDragHelper.captureChildView(mRootView, pointerId);
}
說明:1、tryCaptureView():由用戶決定捕獲哪個子View的行爲,由於我們是移動整個ViewGroup,直接返回false。
2、onViewReleased():當視圖拖拽完成時回調,主要處理鬆手之後視圖最終位置,mViewDragHelper.settleCapturedViewAt(0, 0)方法內部也是通過調用Scroller來實現移動
3、onViewPositionChanged():看方法名稱就知道View位置改變的時候回調,因此可以通過判斷最後的位置是否超過右邊緣來結束activity
4、getViewHorizontalDragRange():獲取當前水平方向的範圍,右滑,設置左邊緣即可
5、clampViewPositionHorizontal():限制水平方向的位置,默認不限制,因此需要重寫限制大於0,這樣就可以實現右滑
6、clampViewPositionVertical():限制垂直方向上的位置,右滑可以不重寫
7、onEdgeDragStarted():無子類捕獲,父類從之前設置的邊緣拖拽的時候回調,此時去捕獲activity的視圖
@Override
public void computeScroll() {
//使用settleCapturedViewAt方法是,必須重寫computeScroll方法,傳入true
//持續滾動期間,不斷刷新ViewGroup
if (mViewDragHelper.continueSettling(true))
ViewCompat.postInvalidateOnAnimation(this);
}
說明:mViewDragHelper.continueSettling(true)方法在拖拽過程中通過返回值不斷的回調,使拖拽過程更加平滑,與mViewDragHelper.settleCapturedViewAt(0, 0)呼應,主要還是利用Scroller的滑動機制。
觸摸範圍
完成上述步驟已經可以實現右滑,只是ViewDragHelper內部檢測是否是邊緣,然後才能去拖拽。ViewDragHelper源碼默認的邊緣是20dp,如果想實現全屏拖拽的效果必須修改此值,查看ViewDragHelper源碼並沒有可以修改邊緣寬度的方法,因此採用反射來實現。
/**
* 設置可以拖拽的觸點範圍
*
* @param touchRange
*/
private void setTouchRange(int touchRange) {
Class<?> aClass = mViewDragHelper.getClass();
Field mDividerHeight = null;
try {
mDividerHeight = aClass.getDeclaredField("mEdgeSize");
mDividerHeight.setAccessible(true);
mDividerHeight.setInt(mViewDragHelper, touchRange);
} catch (Exception e) {
e.printStackTrace();
}
}
總結
本篇採用與上篇不同的方式,主要不同點是事件處理的時機問題和觸摸範圍的修改,還有ViewDragHelper的各個回調方法處理,其他方面沒區別,現已完成測試,效果與上篇完全一致。現在還在做進一步的封裝,後續會持續更新最新代碼。
右滑結束Activity