github上的一個開源項目:https://github.com/BlueMor/DragLayout,
效果圖:
側滑效果的實現主要用的是ViewDragHelper,實現了對TouchEvent中的action處理進行了封裝,
使用者不必操心在側滑的過程中如何處理TouchEvent中的ACTION_DOWN、ACTION_MOVE和ACTION_UP事件,
而只需實現回調接口ViewDragHelper.Callback()中的方法就ok了,在此項目中實現的接口:
private ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {//當前view是否允許拖動
return true;
}
@Override
public int getViewHorizontalDragRange(View child) {<span style="font-family: Arial, Helvetica, sans-serif;">//橫向拖動的最大距離</span>
return width;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);//ACTION_UP事件後調用其方法
if (xvel > 0) {
open();
} else if (xvel < 0) {
close();
} else if (releasedChild == vg_main && mainLeft > range * 0.3) {
open();
} else if (releasedChild == vg_left && mainLeft > range * 0.7) {
open();
} else {
close();
}
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {//返回橫向座標左右邊界值
if (mainLeft + dx < 0) {
return 0 ;//0
} else if (mainLeft + dx > range) {
return range;//
} else {
return left;
}
}
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {//view在拖動過程座標發生變化時會調用此方法,包括兩個時間段:手動拖動和自動滾動
if (changedView == vg_main) {
Log.d("draglayout","changedview is vg main");
mainLeft = left;
} else {
mainLeft = mainLeft + left;
Log.d("draglayout","changedview is vg left");
}
if (mainLeft < 0) {
mainLeft = 0;
} else if (mainLeft > range) {
mainLeft = range;
}
if (isShowShadow) {
iv_shadow.layout(mainLeft, 0, mainLeft + width, height);
}
if (changedView == vg_left) {
vg_left.layout(0, 0, width, height);
vg_main.layout(mainLeft, 0, mainLeft + width, height);
}
dispatchDragEvent(mainLeft);
}
};
在此接口中基本沒有對touchevent事件的處理,那這些方法是怎麼與touchevent時間聯繫起來的?
在touchevent事件處理中DragLayout是直接調用了ViewDragHelper中的processTouchEvent方法:
public boolean onTouchEvent(MotionEvent e) {
try {
dragHelper.processTouchEvent(e);
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
在processTouchEvent中對ACTION_DOWN、ACTION_MOVE和ACTION_UP事件進行了處理:
1.在ACTION_DOWN中調用回調接口中的tryCaptureView方法,看當前touch的view是否允許拖動
在此項目中的是直接return true,兩個view都是允許拖動的
2.在ACTION_MOVE中,view的座標發生改變,調用回調接口中的onViewPositionChanged方法,
根據座標信息對view進行layout,通過ViewHelper這個類中的setScaleX、setScaleY方法,實現在
拖動的過程中view在XY座標上進行相應比例的縮放;
3.在ACTION_UP後調用回調接口中的onViewReleased方法,此方法中一個重要的任務是在ACTION_UP事件
後,實現view的自動滑動,這裏主要是使用了ViewDragHelper中smoothSlideViewTo方法,start了ViewDragHelper
中的mScroller:
mScroller.startScroll(startLeft, startTop, dx, dy, duration);
setDragState(STATE_SETTLING);
然後通過方法ViewCompat.postInvalidateOnAnimation(this)調用view的 computeScroll() 方法:
public void computeScroll() {
if (dragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
ViewDragHelper的中的continueSettling方法會調用的mScroller.computeScrollOffset();方法,
來判斷滾動是否結束,並在獲取mScroller.getCurrX() 的值後,調用回調接口的onViewPositionChanged方法對view進行layout,
自此完成了手動拖和ACTION_UP後自動滾動的兩個過程,大致流程圖如下: