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;
}