- CompoundButton 源码分析
- LinearLayout 源码分析
- SearchView 源码解析
- LruCache 源码解析
- ViewDragHelper 源码解析
- BottomSheets 源码解析
- Media Player 源码分析
- NavigationView 源码解析
- Service 源码解析
- Binder 源码分析
- Android 应用 Preference 相关及源码浅析 SharePreferences 篇
- ScrollView 源码解析
- Handler 源码解析
- NestedScrollView 源码解析
- SQLiteOpenHelper/SQLiteDatabase/Cursor 源码解析
- Bundle 源码解析
- LocalBroadcastManager 源码解析
- Toast 源码解析
- TextInputLayout
- LayoutInflater 和 LayoutInflaterCompat 源码解析
- TextView 源码解析
- NestedScrolling 事件机制源码解析
- ViewGroup 源码解析
- StaticLayout 源码分析
- AtomicFile 源码解析
- AtomicFile 源码解析
- Spannable 源码分析
- Notification 之 Android 5.0 实现原理
- CoordinatorLayout 源码分析
- Scroller 源码解析
- SwipeRefreshLayout 源码分析
- FloatingActionButton 源码解析
- AsyncTask 源码分析
- TabLayout 源码解析
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
4.2. shouldInterceptTouchEvent() 方法的实现
在这里我们假设大家都清楚了 Android
的事件分发机制,如果不清楚请看 这里 ,要想处理触摸事件,我们需要在 onInterceptTouchEvent(MotionEvent ev)
方法里判断是否需要拦截这次触摸事件,如果此方法返回 true
则触摸事件将会交给 onTouchEvent(MotionEvent event)
处理,这样我们就能处理触摸事件了,所以我们在上面的使用方法里会这样写:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mDragger.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { mDragger.processTouchEvent(event); return true; }
这样就将是否拦截触摸事件,以及处理触摸事件委托给 ViewDragHelper
来处理了,所以我们先来看看 ViewDragHelper
中 shouldInterceptTouchEvent();
方法的实现:
public boolean shouldInterceptTouchEvent(MotionEvent ev) { //获取 action final int action = MotionEventCompat.getActionMasked(ev); //获取 action 对应的 index final int actionIndex = MotionEventCompat.getActionIndex(ev); //如果是按下的 action 则重置一些信息,包括各种事件点的数组 if (action == MotionEvent.ACTION_DOWN) { // Reset things for a new event stream, just in case we didn't get // the whole previous stream. cancel(); } //初始化 mVelocityTracker if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); //根据 action 来做相应的处理 switch (action) { case MotionEvent.ACTION_DOWN: { final float x = ev.getX(); final float y = ev.getY(); //获取这个事件对应的 pointerId,一般情况下只有一个手指触摸时为 0 //两个手指触摸时第二个手指触摸返回的 pointerId 为 1,以此类推 final int pointerId = MotionEventCompat.getPointerId(ev, 0); //保存点的数据 //TODO (1) saveInitialMotion(x, y, pointerId); //获取当前触摸点下最顶层的子 View //TODO (2) final View toCapture = findTopChildUnder((int) x, (int) y); //如果 toCapture 是已经捕获的 View,而且正在处于被释放状态 //那么就重新捕获 if (toCapture == mCapturedView && mDragState == STATE_SETTLING) { tryCaptureViewForDrag(toCapture, pointerId); } //如果触摸了边缘,回调 callback 的 onEdgeTouched() 方法 final int edgesTouched = mInitialEdgesTouched[pointerId]; if ((edgesTouched & mTrackingEdges) != 0) { mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); } break; } //当又有一个手指触摸时 case MotionEventCompat.ACTION_POINTER_DOWN: { final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); final float x = MotionEventCompat.getX(ev, actionIndex); final float y = MotionEventCompat.getY(ev, actionIndex); //保存触摸信息 saveInitialMotion(x, y, pointerId); //因为同一时间 ViewDragHelper 只能操控一个 View,所以当有新的手指触摸时 //只讨论当无触摸发生时,回调边缘触摸的 callback //或者正在处于释放状态时重新捕获 View if (mDragState == STATE_IDLE) { final int edgesTouched = mInitialEdgesTouched[pointerId]; if ((edgesTouched & mTrackingEdges) != 0) { mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId); } } else if (mDragState == STATE_SETTLING) { // Catch a settling view if possible. final View toCapture = findTopChildUnder((int) x, (int) y); if (toCapture == mCapturedView) { tryCaptureViewForDrag(toCapture, pointerId); } } break; } //当手指移动时 case MotionEvent.ACTION_MOVE: { if (mInitialMotionX == null || mInitialMotionY == null) break; // First to cross a touch slop over a draggable view wins. Also report edge drags. //得到触摸点的数量,并循环处理,只处理第一个发生了拖拽的事件 final int pointerCount = MotionEventCompat.getPointerCount(ev); for (int i = 0; i < pointerCount; i++) { final int pointerId = MotionEventCompat.getPointerId(ev, i); final float x = MotionEventCompat.getX(ev, i); final float y = MotionEventCompat.getY(ev, i); //获得拖拽偏移量 final float dx = x - mInitialMotionX[pointerId]; final float dy = y - mInitialMotionY[pointerId]; //获取当前触摸点下最顶层的子 View final View toCapture = findTopChildUnder((int) x, (int) y); //如果找到了最顶层 View,并且产生了拖动(checkTouchSlop() 返回 true) //TODO (3) final boolean pastSlop = toCapture != null && checkTouchSlop(toCapture, dx, dy); if (pastSlop) { //根据 callback 的四个方法 getView[Horizontal|Vertical]DragRange 和 //clampViewPosition[Horizontal|Vertical]来检查是否可以拖动 final int oldLeft = toCapture.getLeft(); final int targetLeft = oldLeft + (int) dx; final int newLeft = mCallback.clampViewPositionHorizontal(toCapture, targetLeft, (int) dx); final int oldTop = toCapture.getTop(); final int targetTop = oldTop + (int) dy; final int newTop = mCallback.clampViewPositionVertical(toCapture, targetTop, (int) dy); final int horizontalDragRange = mCallback.getViewHorizontalDragRange( toCapture); final int verticalDragRange = mCallback.getViewVerticalDragRange(toCapture); //如果都不允许移动则跳出循环 if ((horizontalDragRange == 0 || horizontalDragRange > 0 && newLeft == oldLeft) && (verticalDragRange == 0 || verticalDragRange > 0 && newTop == oldTop)) { break; } } //记录并回调是否有边缘触摸 reportNewEdgeDrags(dx, dy, pointerId); if (mDragState == STATE_DRAGGING) { // Callback might have started an edge drag break; } //如果产生了拖动则调用 tryCaptureViewForDrag() //TODO (4) if (pastSlop && tryCaptureViewForDrag(toCapture, pointerId)) { break; } } //保存触摸点的信息 saveLastMotion(ev); break; } //当有一个手指抬起时,清除这个手指的触摸数据 case MotionEventCompat.ACTION_POINTER_UP: { final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex); clearMotionHistory(pointerId); break; } //清除所有触摸数据 case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { cancel(); break; } } //如果 mDragState 等于正在拖拽则返回 true return mDragState == STATE_DRAGGING; }
上面就是整个 shouldInterceptTouchEvent()
的实现,上面的注释也足够清楚了,我们这里就先不分析某一种触摸事件,大家可以看到我上面留了几个 TODO,下文会一起分析,这里我假设大家都已经对触摸事件分发处理都有充分的理解了,我们下面就直接看 ViewDragHelper
里 processTouchEvent()
方法的实现。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论