開(kāi)發(fā)中棘伴,我們經(jīng)常會(huì)有拖動(dòng)View的一些效果,比如
- 1.跟著手指移動(dòng)的小球
- 2.抽屜效果
- 3.手指撥動(dòng)后就返回上一個(gè)頁(yè)面的效果
這些效果如果完全靠自己自定義ViewGroup屁置,然后重寫onTouchEvent以及onInterceptTouchEvent來(lái)完成會(huì)非常的麻煩焊夸,挑戰(zhàn)性很大。但是假如有了ViewGragHelper的幫助蓝角,會(huì)變得簡(jiǎn)單很多阱穗。所以,可以看到ViewDragHelper一般出現(xiàn)在自定義ViewGroup中使鹅,幫助我們快速的處理一些手勢(shì)相關(guān)的效果揪阶。
先上圖看這個(gè)例子:
- 第一個(gè)View,就是演示簡(jiǎn)單的移動(dòng)
- 第二個(gè)View患朱,演示除了移動(dòng)后鲁僚,松手自動(dòng)返回到原本的位置。(注意你拖動(dòng)的越快裁厅,返回的越快)
- 第三個(gè)View冰沙,邊界移動(dòng)時(shí)對(duì)View進(jìn)行捕獲。
實(shí)現(xiàn)步驟
1.自定義ViewGroup执虹,繼承LinearLayout
public class VDHLayout extends LinearLayout
2.在構(gòu)造方法中創(chuàng)建ViewGragHelper對(duì)象拓挥,同時(shí)設(shè)置屏幕左邊緣可以被追蹤:
mDragger = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
.... //這里面會(huì)覆寫很多方法
....//這里面會(huì)覆寫很多方法
....//這里面會(huì)覆寫很多方法
....//這里面會(huì)覆寫很多方法
});
mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);
這里的第二參數(shù)是1.0f代表sensitivity, 主要用于設(shè)置touchslop
3.要想把一系列的事件交給ViewDragHelper處理的話袋励,需要重寫以下方法:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mDragger.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mDragger.processTouchEvent(event);
return true;
}
4.這一步就是要override callback方法撞叽,來(lái)實(shí)現(xiàn)對(duì)ViewGroup下的子View的控制。
4.1獲取子View的對(duì)象
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mDragView = getChildAt(0);
mAutoBackView = getChildAt(1);
mEdgeTrackerView = getChildAt(2);
}
4.2由于第二個(gè)View當(dāng)被拖動(dòng)以后需要回到原來(lái)的位置插龄,所以需要記錄當(dāng)前的位置, 覆寫onLayout方法
private Point mAutoBackOriginPos = new Point();
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mAutoBackOriginPos.x = mAutoBackView.getLeft();
mAutoBackOriginPos.y = mAutoBackView.getTop();
}
4.3由于第三個(gè)View在手指觸摸它的時(shí)候不會(huì)移動(dòng)科展,所以
@Override
public boolean tryCaptureView(View child, int pointerId) {
//mEdgeTrackerView禁止直接移動(dòng)
return child == mDragView || child == mAutoBackView;
}
也就是需要觸發(fā)touch事件的view需要覆寫這個(gè)方法均牢,并且要指定childview
4.4手指釋放后,彈回原來(lái)的位置
//手指釋放的時(shí)候回調(diào)
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//mAutoBackView手指釋放時(shí)可以自動(dòng)回去
if (releasedChild == mAutoBackView) {
mDragger.settleCapturedViewAt(mAutoBackOriginPos.x, mAutoBackOriginPos.y);
invalidate();
}
}
注意由于這里使用的是invalidate方法才睹,所以需要重寫
@Override
public void computeScroll() {
if (mDragger.continueSettling(true)) {
invalidate();
}
}
4.5指定邊界能拖動(dòng)的View
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
mDragger.captureChildView(mEdgeTrackerView, pointerId);
}
4.6當(dāng)想要控制View的移動(dòng)邊界的時(shí)候徘跪,可以:
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//只在ViewGroup的內(nèi)部移動(dòng)
final int leftBound = getPaddingLeft();
final int rightBound = getWidth() - child.getWidth() - getPaddingRight();
//這個(gè)寫法不懂
//我希望只在ViewGroup的內(nèi)部移動(dòng)甘邀,
// 即:最小>=paddingleft,最大<=ViewGroup.getWidth()-paddingright-child.getWidth垮庐。
final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
return newLeft;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
到此松邪,我們列一下所有的Callback方法,看看還有哪些沒(méi)用過(guò)的:
- onViewDragStateChanged
當(dāng)ViewDragHelper狀態(tài)發(fā)生變化時(shí)回調(diào)(IDLE,DRAGGING,SETTING[自動(dòng)滾動(dòng)時(shí)])
- onViewPositionChanged
當(dāng)captureview的位置發(fā)生改變時(shí)回調(diào)
- onViewCaptured
當(dāng)captureview被捕獲時(shí)回調(diào)
onViewReleased 已用
- onEdgeTouched
當(dāng)觸摸到邊界時(shí)回調(diào)哨查。
- onEdgeLock
true的時(shí)候會(huì)鎖住當(dāng)前的邊界逗抑,false則unLock。
onEdgeDragStarted 已用
- getOrderedChildIndex
改變同一個(gè)坐標(biāo)(x,y)去尋找captureView位置的方法寒亥。(具體在:findTopChildUnder方法中)
getViewHorizontalDragRange 已用
getViewVerticalDragRange 已用
tryCaptureView 已用
clampViewPositionHorizontal 已用
clampViewPositionVertical 已用
ok邮府,至此所有的回調(diào)方法都有了一定的認(rèn)識(shí)。
5.項(xiàng)目源碼
源碼參考我的github地址