Android随笔-事件分发机制 一、整体流程U 型传递二、三个核心方法1. dispatchTouchEvent — 事件分发入口所有 View 都有是事件分发的总入口。// Activity.dispatchTouchEvent()publicbooleandispatchTouchEvent(MotionEventev){if(ev.getAction()MotionEvent.ACTION_DOWN){onUserInteraction();// 用户交互回调}// 先交给 Window 处理if(getWindow().superDispatchTouchEvent(ev)){returntrue;// 被消费}// 没人消费Activity 自己处理returnonTouchEvent(ev);}// ViewGroup.dispatchTouchEvent() — 核心逻辑publicbooleandispatchTouchEvent(MotionEventev){booleanhandledfalse;if(onFilterTouchEventForSecurity(ev)){finalintactionev.getAction();// 每次 DOWN 重置状态if(actionMotionEvent.ACTION_DOWN){cancelAndClearTouchTargets(ev);resetTouchState();}// 1. 检查是否拦截finalbooleanintercepted;if(actionMotionEvent.ACTION_DOWN||mFirstTouchTarget!null){interceptedonInterceptTouchEvent(ev);}else{interceptedtrue;// 没有 TouchTarget直接拦截}// 2. 如果不拦截找子 View 分发if(!intercepted){ViewchildfindTouchTarget(...);if(child!null){if(child.dispatchTouchEvent(ev)){mFirstTouchTargetchild;// 记录消费者handledtrue;}}}// 3. 如果没子 View 消费自己处理if(mFirstTouchTargetnull){handledsuper.dispatchTouchEvent(ev);// 调用 View 的 dispatch}}returnhandled;}// View.dispatchTouchEvent()publicbooleandispatchTouchEvent(MotionEventevent){booleanresultfalse;// 1. 先调用 OnTouchListener如果设置了if(mOnTouchListener!null(mViewFlagsENABLED_MASK)ENABLEDmOnTouchListener.onTouch(this,event)){resulttrue;// Listener 消费了}// 2. 如果 Listener 没消费调用 onTouchEventif(!resultonTouchEvent(event)){resulttrue;}returnresult;}2. onInterceptTouchEvent — 事件拦截仅 ViewGroup 有决定是否拦截事件。// ViewGroup.onInterceptTouchEvent()publicbooleanonInterceptTouchEvent(MotionEventev){// 默认返回 false不拦截returnfalse;}// 典型拦截逻辑ScrollView 滑动拦截OverridepublicbooleanonInterceptTouchEvent(MotionEventev){switch(ev.getAction()){caseMotionEvent.ACTION_DOWN:mLastYev.getY();mIsScrollingfalse;returnfalse;// DOWN 不拦截让子 View 有机会点击caseMotionEvent.ACTION_MOVE:floatdyev.getY()-mLastY;if(Math.abs(dy)mTouchSlop){mIsScrollingtrue;returntrue;// 滑动时拦截}returnfalse;caseMotionEvent.ACTION_UP:caseMotionEvent.ACTION_CANCEL:returnmIsScrolling;}returnfalse;}3. onTouchEvent — 事件消费所有 View 都有处理具体事件。// View.onTouchEvent()publicbooleanonTouchEvent(MotionEventevent){// 如果不可点击直接返回 falseif((mViewFlagsENABLED_MASK)DISABLED){returnclickable;}// 处理各种点击效果if(clickable||longClickable){switch(event.getAction()){caseMotionEvent.ACTION_UP:// 触发 performClick() → 调用 OnClickListener.onClick()if((mPrivateFlagsPFLAG_PRESSED)!0){performClick();}break;caseMotionEvent.ACTION_DOWN:// 检查长按checkForLongClick(0);break;caseMotionEvent.ACTION_CANCEL:removeTapCallback();removeLongPressCallback();break;}returntrue;// 可点击消费事件}returnfalse;}三、事件处理优先级同一个 View 内事件处理优先级onTouch() (OnTouchListener)→ return true: 直接消费不调用 onTouchEvent→ return false: 继续往下onTouchEvent()→ return true: 消费事件→ 内部调用 performClick()onClick() (OnClickListener)→ 在 ACTION_UP 触发→ 前提是 onTouchEvent 返回 true优先级onTouch onTouchEvent onClick// 设置 OnTouchListener 会拦截 onClickview.setOnTouchListener(newView.OnTouchListener(){OverridepublicbooleanonTouch(Viewv,MotionEventevent){// return true: 消费事件onClick 不会触发// return false: 不消费继续传给 onTouchEvent → onClickreturntrue;}});四、事件序列与状态机完整事件序列ACTION_DOWN → 确定消费者↓ACTION_MOVE (0~N次) → 直接给已确定的消费者↓ACTION_UP / ACTION_CANCEL → 结束序列状态机规则事件行为DOWN 到达重置所有状态重新决定消费者MOVE 到达直接给已确定的消费者不重新遍历UP/CANCEL 到达给消费者重置状态关键规则DOWN 事件决定后续流向一旦某个 View 消费了 DOWN后续 MOVE/UP 都给它拦截发生在 MOVE父 View 在 MOVE 时拦截 → 子 View 收到 CANCEL → 后续事件都给父requestDisallowInterceptTouchEvent(true)子 View 禁止父 View 拦截DOWN 除外五、常见场景处理1. ScrollView 嵌套 RecyclerView// ScrollView 在 MOVE 时拦截OverridepublicbooleanonInterceptTouchEvent(MotionEventev){switch(ev.getAction()){caseMotionEvent.ACTION_DOWN:returnfalse;// 不拦截 DOWNcaseMotionEvent.ACTION_MOVE:if(isVerticalScroll(ev)){returntrue;// 纵向滑动拦截}returnfalse;}returnfalse;}// 拦截后 RecyclerView 收到 CANCEL停止滑动2. ViewPager 嵌套 ListView// 通过 dx/dy 判断滑动方向OverridepublicbooleanonInterceptTouchEvent(MotionEventev){switch(ev.getAction()){caseMotionEvent.ACTION_DOWN:mLastXev.getX();mLastYev.getY();returnfalse;caseMotionEvent.ACTION_MOVE:floatdxMath.abs(ev.getX()-mLastX);floatdyMath.abs(ev.getY()-mLastY);if(dxdy){returntrue;// 横向滑动ViewPager 拦截}returnfalse;// 纵向滑动ListView 处理}returnfalse;}3. Button 在 ScrollView 中点击: Button 消费 DOWN → 后续 MOVE/UP 给 Button滑动: ScrollView 在 MOVE 时拦截 → Button 收到 CANCEL→ 后续 MOVE/UP 给 ScrollView六、自定义 ViewGroup 拦截示例publicclassMyViewGroupextendsViewGroup{privatefloatmLastY;privatebooleanmIsScrollingfalse;OverridepublicbooleanonInterceptTouchEvent(MotionEventev){switch(ev.getAction()){caseMotionEvent.ACTION_DOWN:mLastYev.getY();mIsScrollingfalse;returnfalse;// 不拦截 DOWN让子 View 有机会点击caseMotionEvent.ACTION_MOVE:floatdyev.getY()-mLastY;if(Math.abs(dy)10){// 滑动阈值mIsScrollingtrue;returntrue;// 拦截滑动}returnfalse;caseMotionEvent.ACTION_UP:caseMotionEvent.ACTION_CANCEL:returnmIsScrolling;}returnfalse;}OverridepublicbooleanonTouchEvent(MotionEventevent){// 处理滑动逻辑switch(event.getAction()){caseMotionEvent.ACTION_MOVE:scrollBy(0,(int)(mLastY-event.getY()));mLastYevent.getY();break;}returntrue;// 消费事件}}七、核心口诀规则说明DOWN 决定消费者从 Activity 逐级下传找到消费 DOWN 的 ViewMOVE/UP 直接给不重新遍历直接给已确定的消费者拦截发生在 MOVE父 View 在 MOVE 时拦截子 View 收到 CANCELonTouch onTouchEvent onClick优先级顺序事件序列不可分割DOWN → [MOVE…] → UP/CANCEL 必须同一个 View 消费

相关新闻

最新新闻

西南 AI 产业从 “原型热” 到 “落地冷” 的破局发展路径

西南 AI 产业从 “原型热” 到 “落地冷” 的破局发展路径

西南地区以成都为核心,已经成长为国内人工智能创新的重要集聚地。依托丰富的高校科研人才、持续活跃的创业氛围、不断完善的科创载体体系,西南 AI 产业呈现出极强的创新活力。各类 AI 创新赛事、技术共创活动持续涌现,开发者、创业者、技术人…

2026/8/31 23:41:06
智能体工程原型如何补齐稳定性边界

智能体工程原型如何补齐稳定性边界

智能体工程原型如何补齐稳定性边界在将大模型(LLM)Demo 或原型转化为生产环境真正可用的产品功能时,团队常遇到“原型阶段 10 分钟搭完,生产落地却花了半年排障”的现象。原型只需展示特定 Positive Case 的效果,而可用…

2026/8/31 23:41:06
云原生交付核心链路应该怎样逐步拆开

云原生交付核心链路应该怎样逐步拆开

云原生交付核心链路应该怎样逐步拆开拆服务不是按目录切文件。先找依赖少、输入输出明确、可以独立扩缩容的边缘能力;把共享数据和强事务留在后面处理,避免用一次拆分制造更多同步问题。 单体应用与部署困局:高启动耗时带来的上线发布瓶颈。 …

2026/8/31 23:41:06
算法学习上线前怎样核对关键边界

算法学习上线前怎样核对关键边界

算法学习上线前怎样核对关键边界解决完 Git 冲突,工作树干净并不等于合并正确。手动选择某一侧内容时,缓存空值处理、鉴权、限流或初始化顺序都可能悄悄丢失。合并检查应该回答:目标分支和当前分支各自想保留的行为还在不在。 先看共同祖先和…

2026/8/31 23:41:06
房贷,40年比30年,吃亏还是便宜

房贷,40年比30年,吃亏还是便宜

有人这样算的:贷款100万元,年利率3%,等额本息。30年:月供约 4216元,总利息约 51.8万元。40年:月供约 3580元,总利息约 71.8万元。也就是说:每个月少还约636元,代价是多还…

2026/8/31 23:41:06
Mini PCIe转PCIe/104 OneBank扩展卡设计与实战指南

Mini PCIe转PCIe/104 OneBank扩展卡设计与实战指南

去年我在给一套老轨交测试台架做功能扩展时,碰到了一个非常典型的嵌入式需求:设备里没有标准PCIe插槽,只预留了PCIe/104 OneBank接口,而客户点名要加一块Mini PCIe形态的加密模块。那台设备内部空间小到连一个全高Mini PCIe都塞得…

2026/8/31 23:36:06