Android事件分发:长按事件与双击事件的实现原理
本文同步发表于我的微信公众号微信搜索程语新视界即可关注每个工作日都有文章更新一、长按事件的源码实现长按事件的触发需要满足手指按下后持续一段时间默认500ms期间没有移动超过阈值期间没有抬起长按事件的核心实现在View.java中主要涉及以下几个关键方法// frameworks/base/core/java/android/view/View.java public class View { // 长按超时时间默认500ms private static final int LONG_PRESS_TIMEOUT 500; // 长按检查的延迟消息 private Runnable mPendingCheckForLongPress; // 触摸事件处理 public boolean onTouchEvent(MotionEvent event) { final int action event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: // 手指按下开始检测长按 if (!clickable) break; // 检查是否已经存在长按检测 if (mPendingCheckForLongPress null) { mPendingCheckForLongPress new CheckForLongPress(); } // 延迟500ms发送长按检测消息 postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout()); break; case MotionEvent.ACTION_MOVE: // 手指移动检查是否超出长按容忍范围 if (pointInView(x, y, mTouchSlop)) { // 在范围内继续保持长按检测 } else { // 移出范围移除长按检测 removeLongPressCallback(); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // 手指抬起或取消移除长按检测 removeLongPressCallback(); break; } } // 移除长按回调 private void removeLongPressCallback() { if (mPendingCheckForLongPress ! null) { removeCallbacks(mPendingCheckForLongPress); } } // 长按检测的Runnable实现 private final class CheckForLongPress implements Runnable { private int mOriginalWindowAttachCount; Override public void run() { if (isPressed() mParent ! null mOriginalWindowAttachCount mWindowAttachCount) { // 触发长按事件 if (performLongClick()) { // 长按消费了事件标记 mHasPerformedLongPress true; } } } } // 执行长按事件 public boolean performLongClick() { return performLongClickInternal(mLongClickX, mLongClickY); } private boolean performLongClickInternal(float x, float y) { // 1. 调用OnLongClickListener if (mOnLongClickListener ! null) { handled mOnLongClickListener.onLongClick(this); } // 2. 如果没有消费显示上下文菜单 if (!handled) { handled showContextMenu(x, y); } return handled; } }1.3 关键类ViewConfiguration// frameworks/base/core/java/android/view/ViewConfiguration.java public class ViewConfiguration { // 长按超时时间 private static final int LONG_PRESS_TIMEOUT 500; // 触摸移动的容差 private static final int TOUCH_SLOP 8; public static int getLongPressTimeout() { return LONG_PRESS_TIMEOUT; } public static int getTouchSlop() { return TOUCH_SLOP; } }1.4 长按事件的完整流程ACTION_DOWN ↓ postDelayed(CheckForLongPress, 500ms) ↓ 在此期间 - 如果移动超过阈值 → removeLongPressCallback - 如果手指抬起 → removeLongPressCallback - 如果手指未动 → CheckForLongPress.run() 在500ms后执行 ↓ performLongClick() ↓ mOnLongClickListener.onLongClick()二、双击事件的源码实现双击事件需要满足两次点击的间隔时间小于阈值默认300ms两次点击的位置距离小于阈值双击事件的核心实现在GestureDetector.java中// frameworks/base/core/java/android/view/GestureDetector.java public class GestureDetector { // 双击超时时间默认300ms private static final int DOUBLE_TAP_TIMEOUT 300; // 双击移动容差 private static final int DOUBLE_TAP_SLOP 100; // 双击检测的Handler private Handler mHandler; // 双击监听器 private OnDoubleTapListener mDoubleTapListener; // 当前手势状态 private int mCurrentDownEvent -1; private MotionEvent mPreviousUpEvent; // 触摸事件处理 public boolean onTouchEvent(MotionEvent ev) { final int action ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: // 手指按下检查是否是双击 if (mDoubleTapListener ! null) { boolean hadTapMessage mHandler.hasMessages(TAP); if (hadTapMessage) { // 如果在TAP超时内再次按下可能是双击 if (isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { // 确实是双击 mStillDown true; mDeferConfirmSingleTap true; mInLongPress false; // 触发双击事件 mDoubleTapListener.onDoubleTap(ev); mDoubleTapListener.onDoubleTapEvent(ev); } } } break; case MotionEvent.ACTION_UP: // 手指抬起检查单击 if (mIsLongPress) { // 长按已经触发不处理单击 break; } if (mTapTimeout 0) { // 发送单击检测消息 mHandler.sendEmptyMessageDelayed(TAP, mTapTimeout); } break; } } // 判断是否是双击 private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp, MotionEvent secondDown) { if (!mAlwaysInTapRegion) { return false; } // 检查时间间隔 final long deltaTime secondDown.getEventTime() - firstUp.getEventTime(); if (deltaTime DOUBLE_TAP_TIMEOUT) { return false; // 时间太长不是双击 } // 检查位置偏移 int deltaX (int) firstDown.getX() - (int) secondDown.getX(); int deltaY (int) firstDown.getY() - (int) secondDown.getY(); if (deltaX * deltaX deltaY * deltaY DOUBLE_TAP_SLOP * DOUBLE_TAP_SLOP) { return false; // 位置变化太大不是双击 } return true; } // 单击检测的Handler private class GestureHandler extends Handler { Override public void handleMessage(Message msg) { switch (msg.what) { case TAP: if (mDoubleTapListener ! null !mStillDown) { // 触发单击事件如果没有双击的话 mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent); } break; case LONG_PRESS: // 触发长按事件 dispatchLongPress(); break; } } } }2.3 关键类ViewConfiguration中的双击常量// frameworks/base/core/java/android/view/ViewConfiguration.java public class ViewConfiguration { // 双击超时时间 private static final int DOUBLE_TAP_TIMEOUT 300; // 双击移动容差 private static final int DOUBLE_TAP_SLOP 100; public static int getDoubleTapTimeout() { return DOUBLE_TAP_TIMEOUT; } public static int getDoubleTapSlop() { return DOUBLE_TAP_SLOP; } }2.4 双击事件的完整流程第一次ACTION_DOWN ↓ 第一次ACTION_UP ↓ postDelayed(TAP, 300ms) // 等待双击 ↓ 如果在300ms内发生第二次ACTION_DOWN ↓ isConsideredDoubleTap() 检查时间和位置 ↓ 如果是双击 → onDoubleTap() ↓ 取消TAP消息 ↓ 如果不是双击 → 触发单次点击确认实现原理事件实现方式超时时间判断条件长按Handler延迟消息500ms时间未移动未抬起双击两次点击的时间差300ms时间位置距离长按事件是通过Handler延迟500ms发送消息实现的双击事件是通过计算两次点击的时间差300ms和位置差100px实现的。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2440466.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!