1.先看一下如何構(gòu)造一個(gè)ViewDragHelper:
public static ViewDragHelper create(@NonNull ViewGroup forParent, @NonNull ViewDragHelper.Callback cb) {
return new ViewDragHelper(forParent.getContext(), forParent, cb);
}
public static ViewDragHelper create(@NonNull ViewGroup forParent, float sensitivity, @NonNull ViewDragHelper.Callback cb) {
ViewDragHelper helper = create(forParent, cb);
helper.mTouchSlop = (int)((float)helper.mTouchSlop * (1.0F / sensitivity));
return helper;
}
private ViewDragHelper(@NonNull Context context, @NonNull ViewGroup forParent, @NonNull ViewDragHelper.Callback cb) {
//獲取觸發(fā)移動(dòng)的最小距離
this.mTouchSlop = vc.getScaledTouchSlop();
//構(gòu)造OverScroller
this.mScroller = new OverScroller(context, sInterpolator);
}
ViewDragHelper的構(gòu)造是私有的,是通過create()構(gòu)造對象的阻问。
它有2個(gè)create(),第一個(gè)傳入了parent,也就是使用ViewDragHelper的View本身苍狰,第二個(gè)是Callback叽粹。然后默認(rèn)調(diào)用第二個(gè)create()蛆楞,第二個(gè)多了一個(gè)參數(shù)sensitivity也物,這個(gè)sensitivity是用來設(shè)置mTouchSlop的叛赚,它值越大mTouchSlop就會(huì)越小澡绩,就會(huì)越敏感,也就是滑動(dòng)的時(shí)候判斷move的間距越短俺附。
create()最后調(diào)用了構(gòu)造肥卡,可以在構(gòu)造方法中看到初始化了一個(gè)OverScroller,可以判斷ViewDragHelper的滑動(dòng)計(jì)算等操作也是通過OverScroller計(jì)算的(參考:Android中Scroller的使用及原理解析)
2. 再看一下ViewDragHelper的Callback
public abstract static class Callback {
public Callback() {
}
//View的拖拽狀態(tài)改變時(shí)觸發(fā)
//STATE_IDLE: 未被拖拽
//STATE_DRAGGING:正在被拖拽
//STATE_SETTLING: 被安放到一個(gè)位置中的狀態(tài)
public void onViewDragStateChanged(int state) {
}
//拖拽時(shí)的(開始移動(dòng))觸發(fā)
//changeView:當(dāng)前被拖拽的view
//left:拖動(dòng)時(shí)left坐標(biāo)
//top:拖動(dòng)時(shí)top坐標(biāo)
//dx:拖拽時(shí)x軸偏移量
//dy:拖拽時(shí)y軸偏移量
public void onViewPositionChanged(@NonNull View changedView, int left, int top, @Px int dx, @Px int dy) {
}
//view被捕獲時(shí)觸發(fā)(也就是按下)
//capturedChild:捕獲的view
//activePointerId:按下手指的id,多指觸控時(shí)會(huì)用到
//一般用于做準(zhǔn)備初始化工作
public void onViewCaptured(@NonNull View capturedChild, int activePointerId) {
}
//view被放下時(shí)觸發(fā)
//releasedChild被放下的view
//xvel:釋放View的x軸方向上的加速度
//yvel:釋放View的y軸方向上的加速度
//一般用于收尾工作
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
}
//邊緣觸摸時(shí)觸發(fā)(需開啟邊緣觸摸)
//edgeFlags:觸摸的位置EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM
//pointerId: 按下手指的id,多指觸控時(shí)會(huì)用到
//使用較少事镣,一般不重寫
public void onEdgeTouched(int edgeFlags, int pointerId) {
}
//是否開啟邊緣觸摸召调,true代表開啟,默認(rèn)不開啟
//edgeFlags:觸摸的位置EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM
//使用較少蛮浑,一般不重寫
public boolean onEdgeLock(int edgeFlags) {
return false;
}
//邊緣觸摸時(shí)觸發(fā)(需開啟邊緣觸摸)
//edgeFlags:觸摸的位置EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM
//pointerId: 按下手指的id,多指觸控時(shí)會(huì)用到
//使用較少唠叛,一般不重寫
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
}
//尋找當(dāng)前觸摸點(diǎn)下的子View時(shí)會(huì)調(diào)用此方法,尋找到的View會(huì)提供給tryCaptureViewForDrag()來嘗試捕獲沮稚。
//如果需要改變子View的遍歷查詢順序可改寫此方法艺沼,例如讓下層的View優(yōu)先于上層的View被選中。
//使用較少蕴掏,一般不重寫
public int getOrderedChildIndex(int index) {
return index;
}
//暫不明確(返回任何值都可以移動(dòng),網(wǎng)上說的都是錯(cuò)的)
//使用較少盛杰,一般不重寫
public int getViewHorizontalDragRange(@NonNull View child) {
return 0;
}
//暫不明確(返回任何值都可以移動(dòng)挽荡,網(wǎng)上說的都是錯(cuò)的)
//使用較少即供,一般不重寫
public int getViewVerticalDragRange(@NonNull View child) {
return 0;
}
//嘗試捕獲被拖拽的view,如果返回true代表可以被拖拽逗嫡,返回false代表不可以被拖拽
//var1:被拖拽的view
//使用時(shí)判斷需要被拖拽的view是否等等于var1。
//一般判斷很多view其中哪些是否可以移動(dòng)時(shí)使用
public abstract boolean tryCaptureView(@NonNull View var1, int var2);
//返回view在水平方向的位置驱证,
//left:當(dāng)前被拖拽的的view要移動(dòng)到的的left值
//dx:移動(dòng)的偏移量
//返回0則無法移動(dòng),通常直接返回left
//一般必須重寫此方法返回left
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
return 0;
}
//返回view在豎直方向的位置抹锄,
//top:當(dāng)前被拖拽的的view要移動(dòng)到的的left值
//dy:移動(dòng)的偏移量
//返回0則無法移動(dòng)逆瑞,通常直接返回top
//一般必須重寫此方法返回top
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
return 0;
}
}
3. ViewDragHelper的使用
- 先看一下常用到的方法
//以松手前的滑動(dòng)速度為初速動(dòng)荠藤,讓捕獲到的View自動(dòng)滾動(dòng)到指定位置。只能在Callback的onViewReleased()中調(diào)用获高。
settleCapturedViewAt(int finalLeft, int finalTop)
//以松手前的滑動(dòng)速度為初速動(dòng)哈肖,讓捕獲到的View在指定范圍內(nèi)fling。只能在Callback的onViewReleased()中調(diào)用谋减。
flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop)
//指定某個(gè)View自動(dòng)滾動(dòng)到指定的位置牡彻,初速度為0,可在任何地方調(diào)用出爹。
smoothSlideViewTo(View child, int finalLeft, int finalTop)
- 再看使用:
初始化并重寫需要用到的方法:
viewDragHelper= ViewDragHelper.create(this, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(@NonNull View view, int i) {
return true;
}
@Override
public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
return left;
}
@Override
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
return top;
}
@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
super.onViewReleased(releasedChild, xvel, yvel);
//讓捕獲到的View自動(dòng)滾動(dòng)到100庄吼,300位置,只能在這里使用這個(gè)方法
viewDragHelper.settleCapturedViewAt(100,300);
//讓捕獲到的View在100严就,100总寻,500,500這個(gè)范圍內(nèi)fling 梢为,只能在這里使用這個(gè)方法
viewDragHelper.flingCapturedView(100,100,500,500);
//指定某個(gè)View自動(dòng)滾動(dòng)到500渐行,500,初速度為0铸董,可在任何地方調(diào)用祟印。
viewDragHelper.smoothSlideViewTo(releasedChild,500,500);
//以上方法必須手動(dòng)去刷新頁面
invalidate();
}
});
以上四個(gè)為常用的方法,然后獲取事件權(quán)限:
@Override
public boolean onInterceptHoverEvent(MotionEvent event) {
return viewDragHelper.shouldInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
viewDragHelper.processTouchEvent(event);
return true;
}
之前說了粟害,ViewDragHelper內(nèi)部是overScroller完成計(jì)算的,那么和overScroller一樣需要重寫computeScroll()一直刷新頁面:
@Override
public void computeScroll() {
super.computeScroll();
//使用continueSettling(true)判斷拖拽是否完成
if (viewDragHelper.continueSettling(true)){
invalidate();
}
}