Android 事件分发实例之右滑结束Activity(二)
前言
本篇同上一篇实现同样的功能,此篇采用之前音乐锁屏壁纸的功能里面使用的ViewDragHelper来实现,最终的效果与上篇效果相同,因此本篇只挑新内容讲解。
具体步骤
事件分发与冲突解决
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean canScrollHorizontally = canScrollHorizontally(-1, this);
if (!canScrollHorizontally) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
说明:上一篇已经说清一个事件里面包含三个小事件,因为此篇主要是把事件交由ViewDragHelper处理,因此处理canScrollHorizontally()方法时,判断条件不能只放在Move事件中处理,否则Down、Up的事件ViewDragHelper接收不到,查看源码知道mViewDragHelper.shouldInterceptTouchEvent(ev)和mViewDragHelper.processTouchEvent(event)方法必须要三个事件同时存在。
滑动处理
通过ViewDragHelper实现滑动效果,主要是通过实现ViewDragHelper的回调ViewDragHelper.Callback实现滑动效果。
@Override
public boolean tryCaptureView(View child, int pointerId) {
return false;
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//当前回调,松开手时触发,比较触发条件和当前的滑动距离
int left = releasedChild.getLeft();
if (left <= mMaxSlideWidth) {
//缓慢滑动的方法,小于触发条件,滚回去
mViewDragHelper.settleCapturedViewAt(0, 0);
} else {
//大于触发条件,滚出去...
mViewDragHelper.settleCapturedViewAt(mScreenWidth, 0);
}
//需要手动调用更新界面的方法
invalidate();
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
mScrollPercent = Math.abs((float) left
/ (mScreenWidth + mEdgeShadow.getIntrinsicWidth()));
//重绘
invalidate();
//当滚动位置到达屏幕最右边,则关掉Activity
if (changedView == mRootView && left >= mScreenWidth) {
mActivity.finish();
}
}
@Override
public int getViewHorizontalDragRange(View child) {
return ViewDragHelper.EDGE_LEFT;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//限制左右拖拽的位移
left = left >= 0 ? left : 0;
return left;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//上下不能移动,返回0
return 0;
}
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
//触发边缘时,主动捕捉mRootView
mViewDragHelper.captureChildView(mRootView, pointerId);
}
说明:1、tryCaptureView():由用户决定捕获哪个子View的行为,由于我们是移动整个ViewGroup,直接返回false。
2、onViewReleased():当视图拖拽完成时回调,主要处理松手之后视图最终位置,mViewDragHelper.settleCapturedViewAt(0, 0)方法内部也是通过调用Scroller来实现移动
3、onViewPositionChanged():看方法名称就知道View位置改变的时候回调,因此可以通过判断最后的位置是否超过右边缘来结束activity
4、getViewHorizontalDragRange():获取当前水平方向的范围,右滑,设置左边缘即可
5、clampViewPositionHorizontal():限制水平方向的位置,默认不限制,因此需要重写限制大于0,这样就可以实现右滑
6、clampViewPositionVertical():限制垂直方向上的位置,右滑可以不重写
7、onEdgeDragStarted():无子类捕获,父类从之前设置的边缘拖拽的时候回调,此时去捕获activity的视图
@Override
public void computeScroll() {
//使用settleCapturedViewAt方法是,必须重写computeScroll方法,传入true
//持续滚动期间,不断刷新ViewGroup
if (mViewDragHelper.continueSettling(true))
ViewCompat.postInvalidateOnAnimation(this);
}
说明:mViewDragHelper.continueSettling(true)方法在拖拽过程中通过返回值不断的回调,使拖拽过程更加平滑,与mViewDragHelper.settleCapturedViewAt(0, 0)呼应,主要还是利用Scroller的滑动机制。
触摸范围
完成上述步骤已经可以实现右滑,只是ViewDragHelper内部检测是否是边缘,然后才能去拖拽。ViewDragHelper源码默认的边缘是20dp,如果想实现全屏拖拽的效果必须修改此值,查看ViewDragHelper源码并没有可以修改边缘宽度的方法,因此采用反射来实现。
/**
* 设置可以拖拽的触点范围
*
* @param touchRange
*/
private void setTouchRange(int touchRange) {
Class<?> aClass = mViewDragHelper.getClass();
Field mDividerHeight = null;
try {
mDividerHeight = aClass.getDeclaredField("mEdgeSize");
mDividerHeight.setAccessible(true);
mDividerHeight.setInt(mViewDragHelper, touchRange);
} catch (Exception e) {
e.printStackTrace();
}
}
总结
本篇采用与上篇不同的方式,主要不同点是事件处理的时机问题和触摸范围的修改,还有ViewDragHelper的各个回调方法处理,其他方面没区别,现已完成测试,效果与上篇完全一致。现在还在做进一步的封装,后续会持续更新最新代码。
右滑结束Activity