ViewDragHelper類的使用方法和事件分發機制的配合
- 寫一個SlideMenu類,繼承自FrameLayout,因為如果繼承自ViewGroup的話,需要我們自己來實作onMeasure方法,而該方法的實作一般比較麻煩且沒有必要,是以選擇繼承系統的已有的控件FrameLayout,不用其他控件是因為FrameLayout最輕量級
- 在布局檔案中給SlideMenu添加2個子布局,分别是菜單的布局和主界面的布局(代碼略);
- 移動View的方法總結:
通過改變View的scroll的坐标來移動: scrollTo(x,y);//滾動到指定位置 scrollBy(xOffset,yOffset);//滾動多少距離
- 通過改變View在父View中的布局的位置:
offsetLeftAndRight(offset);//同時更改view的left和right offsetTopAndBottom(offset);//同時更改view的top和bottom layout(l,t,r,b);
但是谷歌發現很多View移動的情景有相識點, 是以封裝了ViewDragHelper類來幫助我們在ViewGroup中進行子View的移動:
ViewDragHelper類的介紹
谷歌在2013年I/O開發者大會上提出;
- 專門用于在ViewGroup中對子View進行拖拽處理;
- 在19(Android4.4)以及以上的v4包中;
- 本質是封裝了對觸摸事件的解析,包括觸摸位置,觸摸速度以及Scroller的封裝,隻需要我們在回調方法中指定是否移動,移動多少等等,但是需要注意的是:它隻是一個觸摸事件的解析類(如GestureDecetor),是以需要我們傳遞給它觸摸事件,它才能工作;
ViewDragHelper類的使用方法
如何建立ViewDragHelper對象
ViewDragHelper viewDragHelper = ViewDragHelper.create(this, callback);
由于ViewDragHelper隻是觸摸事件解析類,要想讓ViewDragHelper工作,需要将觸摸事件傳遞給它
public boolean onInterceptTouchEvent(MotionEvent ev) {
//讓ViewDragHelper幫助我們判斷是否應該攔截
boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//将觸摸事件傳遞給ViewDragHelper來解析
viewDragHelper.processTouchEvent(event);
return true;
}
重寫幾個方法
-
判斷是否需要捕獲View的觸摸事件
public boolean tryCaptureView(View child, int pointerId)
-
當一個View被捕獲觸摸事件時候調用
public void onViewCaptured(View capturedChild, int activePointerId)
-
方法名為擷取水準方向拖拽的範圍,然而目前并沒有用,該方法的傳回值用來作為判斷滑動方向的條件之一
public int getViewHorizontalDragRange(View child)
-
用來修正或者指定子View在水準方向上的移動
public int clampViewPositionHorizontal(View child, int left, int dx)
-
用來修正或者指定子View在垂直方向上的移動
public int clampViewPositionVertical(View child, int top, int dy)
-
當View移動的時候調用
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy)
-
當手指從View上擡起的時候回調
public void onViewReleased(View releasedChild, float xvel, float yvel)
其他的方法
調用方法 :參數需要移動的控件,左上角的原點的坐标
smoothSlideViewTo(View child, int finalLeft, int finalTop)
方法可以使自動移動到某個位置
重新整理頁面:參數為需要重新整理控件的父控件ViewCompat.postInvalidateOnAnimation(Slidemenu.this);
需要重寫view裡面的computeScroll()方法才能實作重新整理
/**
* 頁面重新整理都得走這個方法
* 重新整理方法->draw()->computeScroll
*/
@Override
public void computeScroll() {
super.computeScroll();
if (viewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(SlideMenu.this);
}
}
水準移動的案例
private ViewDragHelper.Callback callback= new MyCallback();
private class MyCallback extends ViewDragHelper.Callback{
/**
* 判斷是否需要捕獲View的觸摸事件,捕獲事件後才可執行下面的方法
* @param child 目前觸摸的View
* @param pointerId 觸摸點索引
* @return true:捕獲, false:不捕獲.
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
Log.d(TAG, "tryCaptureView");
return true;
}
/**
* 當一個View被捕獲觸摸事件時候調用
* @param capturedChild 被捕獲觸摸事件的子View
* @param activePointerId
*/
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
Log.d(TAG, "tryConViewCapturedaptureView");
super.onViewCaptured(capturedChild, activePointerId);
}
/**
* 方法名為擷取水準方向拖拽的範圍,然而目前并沒有用,該方法的傳回值用來作為判斷滑動方向的條件之一,
* 如果你想水準移動,那麼該方法的傳回值最好大于0
* @param child
* @return
*/
@Override
public int getViewHorizontalDragRange(View child) {
//Log.d(TAG, "getViewHorizontalDragRange");
return 1;
}
/**
* 用來修正或者指定子View在水準方向上的移動
* @param child
* @param left 是ViewDragHelper幫你計算好的View最新的left的值,left=view.getLeft()+dx
* @param dx 本次水準移動的距離
* @return 傳回的值表示我們真正想讓View的left變成的值
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//Log.v(TAG, "clampViewPositionHorizontal");
return left;
}
/**
* 用來修正或者指定子View在垂直方向上的移動
* @param
* @param top 是ViewDragHelper幫你計算好的View最新的top的值,top=view.getTop()+dy
* @param dy 本次垂直移動的距離
* @return 傳回的值表示我們真正想讓View的top變成的值
*/
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
// Log.e(TAG, "clampViewPositionVertical");
return super.clampViewPositionVertical(child, top, dy);
}
/**
* 當View移動的時候調用
* @param changedView 目前移動的VIew
* @param left 目前View移動之後最新的left
* @param top 目前View移動之後最新的top
* @param dx 水準移動的距離
* @param dy 垂直移動的距離
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
Log.d(TAG, "onViewPositionChanged");
if (changedView==mMainView){
}
}
/**
* 當手指從View上擡起的時候回調
* @param releasedChild
* @param xvel x方向滑動的速度
* @param yvel y方向滑動的速度
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
Log.v(TAG, "onViewReleased");
super.onViewReleased(releasedChild, xvel, yvel);
}
};
事件分發機制
幾個重要方法
-
分發事件
public boolean dispatchTrackballEvent(MotionEvent event)
-
攔截事件
public boolean onInterceptTouchEvent(MotionEvent ev)
-
處理事件
public boolean onTouchEvent(MotionEvent event)
ViewGroup中的事件處理的發放
/**
*分發事件
* @param event 事件
*/
@Override
public boolean dispatchTrackballEvent(MotionEvent event) {
return super.dispatchTrackballEvent(event);
}
/**
*攔截事件
* @param event 事件
* @return 傳回true 不攔截 ;false攔截
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(TAG, "是否攔截");
//讓viewDragHelper幫主我們判斷是否應該攔截
return viewDragHelper.shouldInterceptTouchEvent(ev);
}
/**
*處理事件
* @param event 事件
* @return 傳回true 自己處理 ;false傳遞給下一個處理
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent");
//讓ViewDragHelper幫主我們處理觸摸事件
viewDragHelper.processTouchEvent(event);
return true;
}