參考文獻(xiàn):
https://blog.csdn.net/briblue/article/details/73730386
http://www.reibang.com/p/111a7bc76a0e
ViewDragHelper是針對(duì) ViewGroup 中的拖拽和重新定位 views 操作時(shí)提供了一系列非常有用的方法和狀態(tài)追蹤。基本上使用在自定義ViewGroup處理拖拽中煤杀!
所謂無(wú)圖無(wú)真相欺嗤。交掏。沈条。
本文知識(shí)點(diǎn):
- 常用API的說(shuō)明
- ViewDragHelper的使用案例
- 常見(jiàn)的使用案例
1.常用API說(shuō)明
這里為什么先介紹API呢酣难?因?yàn)樯蟻?lái)我貼出來(lái)一堆代碼你都不知道每個(gè)方法什么意思让虐,會(huì)很痛苦的,而且還要跳著看罢荡。所以這里決定先介紹一下API大家先有個(gè)印象赡突,后面說(shuō)到的時(shí)候也不會(huì)那么陌生,好了廢話不多說(shuō)了区赵!
ViewDragHelper的API
-
ViewDragHelper create(ViewGroup forParent, Callback cb)惭缰;一個(gè)靜態(tài)的創(chuàng)建方法,
- 參數(shù)1:出入的是相應(yīng)的ViewGroup
- 參數(shù)2:是一個(gè)回掉(其實(shí)這個(gè)回掉你可以自己在外面實(shí)現(xiàn)笼才,后面在細(xì)說(shuō))
-
shouldInterceptTouchEvent(MotionEvent ev) 處理事件分發(fā)的(怎么說(shuō)這個(gè)方法呢漱受?主要是將ViewGroup的事件分發(fā),委托給ViewDragHelper進(jìn)行處理)
- 參數(shù)1:MotionEvent ev 主要是ViewGroup的事件
- processTouchEvent(MotionEvent event) 處理相應(yīng)TouchEvent的方法骡送,這里要注意一個(gè)問(wèn)題昂羡,處理相應(yīng)的TouchEvent的時(shí)候要將結(jié)果返回為true,消費(fèi)本次事件摔踱!否則將無(wú)法使用ViewDragHelper處理相應(yīng)的拖拽事件虐先!
基本上使用到的就這三個(gè)API就能實(shí)現(xiàn)相應(yīng)的拖拽效果了!(有的API我沒(méi)有查看派敷,好像還有很多API但是有的真的不怎么理解蛹批!原諒我的放蕩不羈。篮愉。腐芍。)
ViewDragHelper.Callback的API(也就是創(chuàng)建ViewDragHelper傳入的回調(diào)方法)
其實(shí)這個(gè)回掉主要是用來(lái)監(jiān)聽(tīng)一些內(nèi)容的,其實(shí)你可以這樣试躏,自己實(shí)現(xiàn)一個(gè)類猪勇,繼承這個(gè)類,然后在里面寫相應(yīng)的邏輯冗酿,這樣代碼能比較整潔埠对!也便于其他人的觀看
-
tryCaptureView(View child, int pointerId) 這是一個(gè)抽象類,必須去實(shí)現(xiàn)裁替,也只有在這個(gè)方法返回true的時(shí)候下面的方法才會(huì)生效项玛;
- 參數(shù)1:捕獲的View(也就是你拖動(dòng)的這個(gè)View)
- 參數(shù)2:這個(gè)參數(shù)我也不知道什么意思API中寫的一個(gè)什么指針,這里沒(méi)有到也沒(méi)有注意
-
onViewDragStateChanged(int state) 當(dāng)狀態(tài)改變的時(shí)候回調(diào)弱判,返回相應(yīng)的狀態(tài)(這里有三種狀態(tài))
- STATE_IDLE 閑置狀態(tài)
- STATE_DRAGGING 正在拖動(dòng)
- STATE_SETTLING 放置到某個(gè)位置
-
onViewPositionChanged(View changedView, int left, int top, int dx, int dy) 當(dāng)你拖動(dòng)的View位置發(fā)生改變的時(shí)候回調(diào)
- 參數(shù)1:你當(dāng)前拖動(dòng)的這個(gè)View
- 參數(shù)2:距離左邊的距離
- 參數(shù)3:距離右邊的距離
- 參數(shù)4:x軸的變化量
- 參數(shù)5:y軸的變化量
-
onViewCaptured(View capturedChild, int activePointerId)捕獲View的時(shí)候調(diào)用的方法
- 參數(shù)1:捕獲的View(也就是你拖動(dòng)的這個(gè)View)
- 參數(shù)2:這個(gè)參數(shù)我也不知道什么意思API中寫的一個(gè)什么指針襟沮,這里沒(méi)有到也沒(méi)有注意
-
onViewReleased(View releasedChild, float xvel, float yvel) 當(dāng)View停止拖拽的時(shí)候調(diào)用的方法,一般在這個(gè)方法中重置一些參數(shù),比如回彈什么的开伏。膀跌。。
- 參數(shù)1:你拖拽的這個(gè)View
- 參數(shù)2:x軸的速率
- 參數(shù)3:y軸的速率
-
clampViewPositionVertical(View child, int top, int dy) 豎直拖拽的時(shí)候回調(diào)的方法
- 參數(shù)1:拖拽的View
- 參數(shù)2:距離頂部的距離
- 參數(shù)3:變化量
-
clampViewPositionHorizontal(View child, int left, int dx) 水平拖拽的時(shí)候回調(diào)的方法
- 參數(shù)1:拖拽的View
- 參數(shù)2:距離左邊的距離
- 參數(shù)3:變化量
基本上常用的API就這么多固灵,也都解釋了相應(yīng)參數(shù)代表的意思捅伤,可能有些不是那么準(zhǔn)確,還請(qǐng)指正巫玻。丛忆。。
2. ViewDragHelper的使用案例
2.1案例一:
自定義一個(gè)LinearLayout使其內(nèi)部的View可以進(jìn)行相應(yīng)的拖動(dòng)
2.1.1 創(chuàng)建相應(yīng)的回調(diào)CallBack(其實(shí)就是創(chuàng)建一個(gè)相應(yīng)的ViewDragHelper.Callback對(duì)象)這個(gè)主要是用于把代碼拆分到外面去仍秤。熄诡。。代碼如下
public class MyDragHelper extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View child, int pointerId) {
//注意這里一定要返回true诗力,否則后續(xù)的拖拽回調(diào)是不會(huì)生效的
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
}
這里主要處理了內(nèi)部控件的拖拽問(wèn)題凰浮;
2.1.2自定義一個(gè)LinearLayout,授權(quán)相應(yīng)的拖拽苇本。代碼如下:
public class MyDragLinearLayout extends LinearLayout {
private ViewDragHelper mViewDragHelper;
public MyDragLinearLayout(Context context) {
this(context, null);
}
public MyDragLinearLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyDragLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
MyDragHelper dragHelper = new MyDragHelper();
mViewDragHelper = ViewDragHelper.create(this, dragHelper);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
}
上面這段代碼就是在View初始話的時(shí)候袜茧,創(chuàng)建了一個(gè)回調(diào)和一個(gè)ViewDragHelper對(duì)象,在onInterceptTouchEvent和onTouchEvent處理一下相應(yīng)的邏輯(這里有一點(diǎn)千萬(wàn)要注意就是在onTouchEvent的時(shí)候一定要返回true圈澈,切記1怪堋!康栈!)
然后直接在布局文件中使用這個(gè)自定義的ViewGroup就可以了递递。
3.常見(jiàn)的使用案例
1.回彈效果
回彈效果是最常見(jiàn)的了,也是最主要的內(nèi)容就是回彈的效果I睹础5俏琛!其實(shí)使用方法和上面是類似的悬荣!
1.1委托相應(yīng)的服務(wù)是一樣的菠秒,這里就直接貼上相應(yīng)的代碼了。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
1.2獲取捕獲的View氯迂,并獲取捕獲時(shí)候的位置信息
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
mLeft = capturedChild.getLeft();
mTop = capturedChild.getTop();
}
這里主要是通過(guò)相應(yīng)的捕獲的回調(diào)獲取相應(yīng)的位置信息践叠,主要是作用是為了之后回彈到原始位置提供相應(yīng)的位置信息;
1.3在釋放的時(shí)候重置這個(gè)位置信息
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
mViewDragHelper.settleCapturedViewAt(mLeft, mTop);
invalidate();
}
這里主要是通過(guò)ViewDragHelper的settleCapturedViewAt(int finalLeft, int finalTop)方法使你拖拽的View回到上面獲取的原始位置了嚼蚀。禁灼。。但是其實(shí)這樣是不行的轿曙,為什么呢弄捕?僻孝??上面這個(gè)確實(shí)是設(shè)置拖拽View位置的守谓,但是查看API的時(shí)候有這樣一段描述信息
If this method returns true, the caller should invoke {@link #continueSettling(boolean)}
on each subsequent frame to continue the motion until it returns false. If this method
returns false there is no further work to do to complete the movement.
簡(jiǎn)單的翻譯一下:
如果這個(gè)方法返回true,調(diào)用者應(yīng)該調(diào)用{ @link #continueSettling(boolean)}在每個(gè)后續(xù)幀繼續(xù)運(yùn)動(dòng),直到返回false穿铆。如果這個(gè)方法返回false,沒(méi)有進(jìn)一步的工作來(lái)完成運(yùn)動(dòng)。
也就是說(shuō)這里你要通過(guò)這個(gè)方法進(jìn)行相應(yīng)的處理斋荞,但是怎么處理呢荞雏?請(qǐng)看下面這段代碼:
@Override
public void computeScroll() {
if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
invalidate();
}
}
這個(gè)是重寫的ViewGroup的方法,主要是用于ViewGroup中更新相應(yīng)View的(通過(guò)ScrollX
和ScrollY)通過(guò)上面的代碼就能實(shí)現(xiàn)回彈了平酿。整體代碼如下:
public class MyDragLinearLayout extends LinearLayout {
private ViewDragHelper mViewDragHelper;
public MyDragLinearLayout(Context context) {
this(context, null);
}
public MyDragLinearLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyDragLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mViewDragHelper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {
private int mLeft;
private int mTop;
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
mLeft = capturedChild.getLeft();
mTop = capturedChild.getTop();
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return top;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
mViewDragHelper.settleCapturedViewAt(mLeft, mTop);
invalidate();
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
invalidate();
}
}
}
其實(shí)對(duì)這個(gè)API也只是表層的理解讯檐,如有什么不對(duì)的地方請(qǐng)指出。染服。。