Android 7.0(Nougat)拖放事件派发缺陷的复现与修复:基于 cw-omnibus 仓库 DragDrop/Nougat 示例源码剖析
示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载本文基于开源仓库 cw-omnibusThe Busy Coders Guide to Android Development全量示例源码中的 DragDrop/Nougat 示例工程完整还原该工程在 Android 7.0API 24上暴露的拖放Drag-and-Drop事件派发缺陷系统不再把拖放事件派发给视图层级中所有注册了监听器的容器。通过逐一剖析MainActivity、BugActivity、InclusiveActivity三个入口以及DropTarget包装器源码你将掌握缺陷的触发条件、肉眼可验证的复现步骤以及一套不依赖系统回调、可在 Nougat 及更高版本上恢复旧有行为的通用修复方案。缺陷背景Android 7.0 对拖放事件派发的行为变更示例工程的 README.markdown 开宗明义这是一个用于演示Android 7.0 上拖放处理缺陷的示例应用对应《The Busy Coders Guide to Android Development》一书的拖放章节。在 Android 7.0API 24之前拖放过程中系统会把DragEvent派发给当前窗口视图层级中所有注册了View.OnDragListener的视图——无论拖拽指针是否真正悬停在它们上方。这带来一个隐含契约父容器即使不直接处于拖拽点正下方也能感知ACTION_DRAG_STARTED、ACTION_DRAG_ENTERED、ACTION_DRAG_EXITED等事件从而做出进入高亮、退出复原等 UI 反馈。从 Android 7.0 开始系统仅把拖放事件派发给拖拽点正下方、位于视图栈顶层的那一个视图。因此当拖拽指针停留在某个子视图上方时其父容器上注册的OnDragListener将收不到任何事件——这正是本示例所称的“Android 7.0 上的拖放缺陷”。示例工程同时提供了裸复现BugActivity与修复方案InclusiveActivityDropTarget用一份代码把问题、现象和补丁完整呈现出来。工程全景三种演示场景与构建配置工程位于 DragDrop/Nougat共包含四个 Java 类与一个辅助类职责划分如下文件角色对应布局说明AbstractDragDropDemoActivity.java抽象基类main.xml拖放发起、事件处理、菜单、状态保存的统一实现MainActivity.java基准演示simple.xml只有单个拖放目标行为正确BugActivity.java缺陷复现bug.xml在嵌套容器上注册监听器暴露 Nougat 缺陷InclusiveActivity.java修复演示bug.xml通过DropTarget包装器恢复全部容器的反馈DropTarget.java修复核心—手动沿父链向上补发拖放事件三个 Activity 通过 AndroidManifest.xml 注册其中MainActivity是启动入口另两个通过 actions.xml 的溢出菜单切换每个 Activity 在菜单中隐藏自己的入口项。构建配置方面app/build.gradle 声明compileSdkVersion 24、minSdkVersion 23、targetSdkVersion 24并依赖 Picasso 2.5.2图片加载与com.commonsware.cwac:provider:0.4.2以content://URI 暴露 assets 资源。整体采用 Gradle 4.4 wrapper见 gradle-wrapper.properties与 AGP 3.1.4。需要特别说明的是缺陷现象只在 Android 7.0API 24及以上的设备或模拟器上才能观察到在低版本设备上BugActivity与InclusiveActivity的表现将完全相同。复现步骤如何在设备上亲眼验证缺陷将工程导入 Android Studio或使用 Gradle wrapper 执行构建安装到一台 API 24 的设备/模拟器。进入BugActivity菜单 “Bug”。界面由两层带内边距的嵌套容器与一个铺满内层容器的缩略图组成各容器背景色分别为橙色外层、蓝色内层、紫色图片。长按顶部图片发起拖拽将其拖入下方嵌套容器区域。观察现象在 Android 7.0 上无论拖拽指针如何移动外层与内层容器都不会变为“就绪绿色”而在 Android 6.0 及以下拖拽路径上的容器会依次变绿、移出后复原。返回InclusiveActivity菜单 “Inclusive”重复同样的拖拽所有容器都会正确出现绿色高亮——缺陷已被修复。绿色高亮色值定义在 colors.xmldrop_target_ready#64dd17各容器常态色分别为outer_normal#ffa000、inner_normal#00b0ff、image_normal#d500f9。发起拖放startDragAndDrop、ClipData 与自定义拖影拖放的起点在 AbstractDragDropDemoActivity.java 的onLongClick()图片加载完成后注册OnLongClickListener用户长按即发起拖放。关键代码onLongClick第 102-120 行Uri uriPROVIDER .buildUpon() .appendEncodedPath(StreamProvider.getUriPrefix(AUTHORITY)) .appendEncodedPath(assets/FreedomTower-Morning.jpg) .build(); ClipData clipClipData.newRawUri(getString(R.string.msg_photo), uri); View.DragShadowBuilder shadownew ThumbDragShadow(); if (Build.VERSION.SDK_INTBuild.VERSION_CODES.N) { iv.startDragAndDrop(clip, shadow, Boolean.TRUE, 0); } else { iv.startDrag(clip, shadow, Boolean.TRUE, 0); }这里体现了三个要点拖放载荷ClipData.newRawUri()构造携带text/uri-listMIME 类型的 URI 载荷载荷指向一个content://URI。该 URI 由 Manifest 中注册的StreamProvider提供AndroidManifest.xml 中authorities${applicationId}.provider其路径映射配置在 provider.xml通过asset nameassets /把assets/FreedomTower-Morning.jpg即工程 assets 下的真实图片资源暴露给拖放接收方。版本分支Android 7.0 新增View.startDragAndDrop()支持跨 Activity、跨应用的拖放而旧 APIstartDrag()在 N 上已被标记废弃示例用Build.VERSION.SDK_INT做运行时兼容分支。第三个参数Boolean.TRUE是随拖拽携带的localState用于拖放双方在同一个进程内传递对象。自定义拖影内部类ThumbDragShadow第 225-237 行继承View.DragShadowBuilder把阴影缩小到原图 1/8iv.getWidth()/8、iv.getHeight()/8触点居中并把图片本身绘制成阴影外观。接收拖放onDrag 回调与容器高亮反馈基类同时实现了View.OnDragListener其 onDrag()第 180-223 行 是接收端的事件处理器ACTION_DRAG_STARTED校验localState非空且 ClipDescription 包含MIME_TYPE_TEXT_URILIST不满足则返回false拒绝接收本次拖放ACTION_DRAG_ENTERED把容器背景置为就绪绿色ACTION_DRAG_EXITED/ACTION_DRAG_ENDED恢复原色ACTION_DROP若目标视图是ImageView在 API 24 上先调用requestDragAndDropPermissions(event)再从event.getClipData().getItemAt(0)取出 URI交由showThumbnail()用 Picasso 渲染为缩略图。其中 requestDragAndDropPermissions第 209-211 行 是 Nougat 引入的权限 API当拖放载荷来自其他应用的内容 URI 时接收方必须在丢弃事件回调内请求临时读取权限否则无法读取该 URI——这正是工程姊妹示例 DragDrop/Permissions跨应用拖放的核心知识点在本示例中以一行代码先行埋下伏笔。缺陷复现BugActivity 的裸监听器BugActivity.java 是缺陷的最小复现单元。它沿用 bug.xml 的嵌套结构——外层FrameLayout outer_container48dp 内边距包裹内层FrameLayout inner_container48dp 内边距内层再铺满ImageView thumbnail_large——并在三个视图上逐一注册监听器image(ImageView)findViewById(R.id.thumbnail_large); image.setOnDragListener(this); findViewById(R.id.outer_container).setOnDragListener(this); findViewById(R.id.inner_container).setOnDragListener(this);这套写法在 Android 6.0 及以下运行完全正常拖拽指针经过哪一层哪一层就触发ACTION_DRAG_ENTERED并变绿。但在 Android 7.0 上事件只派发给拖拽点正下方最顶层的视图——当指针停留在铺满内层的ImageView上方时两个容器收不到任何事件高亮反馈就此失效。这就是 README 所指的缺陷本体。修复方案DropTarget 包装器的父链补发机制InclusiveActivity.java 换用 DropTarget.java 注册监听器new DropTarget() .on(image, findViewById(R.id.outer_container), findViewById(R.id.inner_container)) .to(this);DropTarget是一个可链式调用的OnDragListener包装器其核心逻辑在 onDrag()第 41-60 行if (Build.VERSION.SDK_INTBuild.VERSION_CODES.N) { return(listener.onDrag(view, dragEvent)); } boolean resultlistener.onDrag(view, dragEvent); ViewParent parentview.getParent(); while (parent!null parent instanceof View) { View parentView(View)parent; if (views.contains(parentView)) { listener.onDrag(parentView, dragEvent); } parentparentView.getParent(); }其设计意图非常清晰低版本直通在 Android 6.0 及以下系统本身就会把事件派发给所有注册视图因此直接透传给真正的监听器保持旧行为零开销Nougat 及以上补发先让最顶层视图按正常流程处理事件返回值result以此为准随后沿getParent()链向上遍历凡是被.on(...)注册过的祖先视图都手动补发一次相同的DragEvent从而把“系统只通知一个视图”收缩为“通知整条注册链上的所有视图”。从源码结构可以推断views.contains(parentView)的注册表设计让补发范围精确限定在用户显式注册的容器集合内避免误伤层级中无关的父视图result保留顶层视图的处理结论则保证了事件消费语义如拒绝拖放不被祖先的返回值覆盖。这一包装器用约 60 行代码把 Android 7.0 被系统削掉的“广播式派发”能力在应用层完整找回是理解事件派发机制与“面向系统行为变更做兼容层”的极佳范本。布局与横竖屏适配接收区由 main.xml 定义上方为拖放源图片asset纵向布局中占一半高度下方为FrameLayout drop_frame在onCreate()中通过getDropFrameContentId()动态填充三种场景各自的布局第 70-73 行。横屏时 layout-land/main.xml 改为左右并排orientationhorizontal、等宽layout_weight1。值得注意的是bug.xml采用 48dp 的内边距刻意制造“指针悬停在子视图上方、却越过父容器”的典型情形——父容器的可接收区域被其子视图完整覆盖这正是 Nougat 行为变更后最容易踩坑的布局形态也使缺陷在真机上极易被触发。总结DragDrop/Nougat是一个“小而完整”的缺陷驱动型示例它以最简布局复现 Android 7.0 拖放事件派发从“全体广播”到“单点投递”的行为变更并给出了一套可复用的工程化解法DropTarget沿父链补发。对于正在适配 API 24 的应用而言它提示了一个容易被忽略的兼容点——凡依赖父容器收到ACTION_DRAG_ENTERED/EXITED来驱动 UI 反馈的拖放实现都必须自行处理父链派发而不能假设系统仍会通知所有注册视图。仓库中同属该主题的姊妹示例 DragDrop/Simple应用内基础拖放、DragDrop/Legacy 与 DragDrop/PermissionsAndroid 7.0 跨应用拖放可与本文对照阅读构成从基础 API 到权限模型再到兼容性修复的完整知识链条。赞分享示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载相关推荐cw-omnibus 跨应用拖放实战基于 Android 7.0 的 Drag-and-Drop 双端实现解析cw omnibus 跨应用拖放实战基于 Android 7.0 的 Drag and Drop 双端实现解析 本篇文章围绕开源仓库 cw omnibus示例工程教程Android 三栏联动界面Three-Pane UI实现剖析基于 cw-omnibus 的 ThreePane 动画示例Android 三栏联动界面Three Pane UI实现剖析基于 cw omnibus 的 ThreePane 动画示例 本文以 cw omnibus示例工程教程深入 Android 7.0 的 AlarmManager.OnAlarmListener 回调式闹钟cw-omnibus Listener 示例全解深入 Android 7.0 的 AlarmManager.OnAlarmListener 回调式闹钟cw omnibus Listener 示例全解 cw示例工程教程上一篇ViTPose/MMPose 数据前处理流水线Data Pipeline完全指南从原理到自定义扩展下一篇Reflex 文档站工程指南基于 reflex_docs 包的项目结构、开发命令与页面白名单机制创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

CEF离屏渲染与D3D11纹理共享:cef-mixer工程实战解析

CEF离屏渲染与D3D11纹理共享:cef-mixer工程实战解析

简介:一份基于 Chromium 嵌入框架的离屏渲染示例工程,面向需要将网页技术集成到桌面程序中的开发者,尤其适合研究高性能渲染与硬件加速的 C 工程师。工程演示了如何让浏览器页面在窗口外完成绘制,再把渲染结果通过图形接口交给图形…

2026/10/15 21:11:16 阅读更多 →
Qwen3+Qwen Agent 智能体开发实战:把 MCP 工具配置改到 TaoToken 的完整流程(一)

Qwen3+Qwen Agent 智能体开发实战:把 MCP 工具配置改到 TaoToken 的完整流程(一)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 21:11:16 阅读更多 →
MinGW-w64离线包部署指南:线程模型、异常模型与PATH配置

MinGW-w64离线包部署指南:线程模型、异常模型与PATH配置

简介:一份适用于Windows平台的MinGW-w64 x86_64 V8.1.0 win32-seh离线安装包,面向需要在Windows环境下编写、编译C/C项目的开发者,解决传统MinGW缺少64位支持及多线程模型兼容问题。该工具链基于GNU编译器套件GCC与binutils,集成标…

2026/10/15 21:11:16 阅读更多 →

最新新闻

Python工具链Rust化:五个提升开发效率的新库实战指南

Python工具链Rust化:五个提升开发效率的新库实战指南

这两年 Python 生态里的话题,绕不开一个词:Rust 化。从包管理到 lint 再到数据验证,一堆新库选择直接用 Rust 重写底层,把之前 Python 工具链里让人头疼的慢、复杂、配置繁琐的问题挨个收拾了一遍。我最近在几个项目里把工具链切到…

2026/10/15 23:45:00 阅读更多 →
Unity输入与屏幕设置实战笔记:从旧版Input到Input System

Unity输入与屏幕设置实战笔记:从旧版Input到Input System

我不太喜欢写那种截图铺满的入门教程,这篇更偏向我自己反复会翻的备忘录。主题就两个:一是Unity里的按键操作,二是屏幕设置。搞游戏开发或者做工具类应用的人应该都有同感,这两种需求几乎每个项目都会碰上,但每次写都还…

2026/10/15 23:45:00 阅读更多 →
五个Python新库盘点:uv、marimo、FastHTML、instructor、textual

五个Python新库盘点:uv、marimo、FastHTML、instructor、textual

Python 生态这两年的迭代速度,说实话比我刚入行那会儿快太多了。以前盯住一两个框架就能吃很久,现在隔一阵子不刷社区,就会冒出一批名字崭新的库。新库多不稀奇,稀奇的是其中真正值得花时间去跟的,永远是少数。今天这篇…

2026/10/15 23:45:00 阅读更多 →
基于Spring Boot的Web图书管理系统开题报告写作指南

基于Spring Boot的Web图书管理系统开题报告写作指南

写开题报告最怕什么?不是查重太高,而是被指导老师问一句“你这个基于Spring Boot的Web图书管理系统,到底要解决什么问题?”当场愣住。我帮人改过不少开题报告,发现真正写得稳的,往往不是技术最炫的&#xf…

2026/10/15 23:45:00 阅读更多 →
QMUI iOS 深入解读:基于全局配置表与丰富控件的 UI 开发提效方案

QMUI iOS 深入解读:基于全局配置表与丰富控件的 UI 开发提效方案

移动开发UI组件 【免费下载链接】QMUI_iOS QMUI iOS——致力于提高项目 UI 开发效率的解决方案 项目地址: https://gitcode.com/gh_mirrors/qm/QMUI_iOS 点击查看 免费下载 QMUI iOS 是由腾讯出品的开源 iOS UI 解决方案,核心目标是通过「一份全局配置表…

2026/10/15 23:45:00 阅读更多 →
PyTorch深度学习实战:从环境搭建到模型训练与调试的完整指南

PyTorch深度学习实战:从环境搭建到模型训练与调试的完整指南

1. 从零上手PyTorch:为什么它成了深度学习项目的默认起点如果你最近打算认真做点深度学习相关的项目,不管是图像分类、文本生成还是推荐系统,大概率绕不开PyTorch这个名字。我身边不少做算法的朋友,从早期用别的框架折腾静态图&am…

2026/10/15 23:43:59 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →