# SharedViewDemo **Repository Path**: itgsa_sharedviewkit/SharedViewDemo ## Basic Information - **Project Name**: SharedViewDemo - **Description**: SharedViewDemo — SharedView Kit 示例应用,演示一镜到底无缝转场动画 KIT 的接入方式与 API 使用方法,供三方应用开发者参考。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 0 - **Created**: 2026-06-08 - **Last Updated**: 2026-10-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SharedViewDemo 一镜到底无缝动画 KIT(SharedViewKit)的 Android 示例应用,演示列表卡片点击展开到详情页、返回时刷新落点等连续过渡动画的接入方式与 API 使用方法,供三方应用开发者参考。 ## 总览 ``` 金标联盟 (ITGSA) 制定的一镜到底无缝动画标准 │ └── SharedView Kit(官方产品名) │ Maven 坐标: gsai.sdk:sharedview-kit(版本始终取 Nexus 最新发布) │ Nexus 仓库: http://nexus.itgsa.com:5566/repository/sharedViewKit/ │ └── SharedViewDemo (联盟示例应用, applicationId: com.example.myapplication) ``` ## 目录结构 ``` SharedViewDemo/ ├── app/ # 示例应用模块 │ └── src/main/java/com/example/myapplication/ │ ├── view/ # 列表页 / 详情页 Activity │ ├── presenter/ # 动画启动与返回刷新逻辑(HomePresenter) │ ├── adapter/ # 列表适配器 │ ├── model/ # 数据模型 │ └── RoundViewOutlineProvider.kt # 圆角 Outline 示例 ├── settings.gradle # Nexus 仓库声明 └── README.md ``` --- ## SDK 产物与仓库信息 ### SDK 产物 | 项目 | 值 | |------|------| | Maven 坐标 | `gsai.sdk:sharedview-kit`(始终使用 Nexus 最新发布版) | | Gradle 依赖声明 | `implementation 'gsai.sdk:sharedview-kit:+'`(动态版本,自动解析 Nexus 最新) | ### Maven 仓库 SharedView Kit SDK 发布在金标联盟 Nexus 仓库: > **注意**:当前仓库地址为 HTTP 协议(`allowInsecureProtocol = true`),属内测阶段部署,正式对外发布前将升级为 HTTPS。 ```groovy // settings.gradle(dependencyResolutionManagement 中) maven { url 'http://nexus.itgsa.com:5566/repository/sharedViewKit/' allowInsecureProtocol = true content { includeGroup 'gsai.sdk' } } ``` ### 运行时支持判断 SharedViewDemo 不按设备品牌或系统版本硬编码分支。应用启动一镜到底动画前,应以 `ViewAnimationManager.isViewAnimationSupported()` 的返回值作为运行时能力判断: | 返回值 | 含义 | |------|------| | `> 0` | 当前设备声明支持 SharedViewKit 能力,返回值为协议版本 | | `0` | 当前设备未声明支持,应用应回退到普通 `startActivity` / `finish` | --- ## 接入依赖 公共 API 包名: ```java import com.itgsa.animation.sharedview.ViewAnimationConfig; import com.itgsa.animation.sharedview.ViewAnimationExtras; import com.itgsa.animation.sharedview.ViewAnimationListener; import com.itgsa.animation.sharedview.ViewAnimationManager; import com.itgsa.animation.sharedview.ViewAnimationType; ``` **app/build.gradle**(Maven / 本地 aar 二选一,用 `-PkitRepo` 切换通道): ```groovy dependencies { // 通道 A(推荐):Maven 仓库(settings.gradle 已声明 Nexus) // 默认 '+' = 动态版本,始终拉取 Nexus 最新发布,与接入 advisor skill // "始终使用最新发布版" 的推荐一致;仅在任务明确要求时才用 -PkitVersion 钉版本 implementation "gsai.sdk:sharedview-kit:+" // 通道 B:本地 aar,随仓附带于 app/libs/,必须显式 -PkitVersion=<版本> // implementation files("libs/sharedview-kit-${kitVersion}.aar") } ``` **通道 A:Maven 集成(默认推荐)**: ```bash ./gradlew :app:assembleDebug -PkitRepo=maven # 自动拉 Nexus 最新 ./gradlew :app:assembleDebug -PkitRepo=maven -PkitVersion=<版本> # 仅在需要钉住时使用 ``` Gradle 动态版本(`+`)有缓存:想强制刷新最新,构建时加 `--refresh-dependencies`。 **通道 B:本地 aar 集成**(适用于无法使用 Maven 仓库的应用): SharedViewKit 代码不开源,但本仓已随仓库附带当前版本 KIT aar(`app/libs/`), clone 本仓即可获得,无需额外获取渠道: ```bash ./gradlew :app:assembleDebug -PkitVersion= # 本地通道必须指明版本 ``` 1. aar 已在 `app/libs/` 目录中 2. KIT 版本升级时,将新 aar 放入 `app/libs/` 并用对应的 `-PkitVersion` 引用 两种通道使用同一版本产物,运行时行为无差别。 > - 使用 `implementation` 引用,Kit 公开 API 在编译期提供类型检查,运行时由设备侧 ROM 提供实现。 > - minSdk 34 | compileSdk / targetSdk 35(demo 配置;接入方可按自身需求调整,KIT 要求 minSdk ≥ 26)。 --- ## 快速接入 ### 1. View 源启动动画 ```java import com.itgsa.animation.sharedview.ViewAnimationConfig; import com.itgsa.animation.sharedview.ViewAnimationListener; import com.itgsa.animation.sharedview.ViewAnimationManager; import android.app.ActivityOptions; if (ViewAnimationManager.isViewAnimationSupported() == 0) { startActivity(intent); return; } ViewAnimationConfig config = new ViewAnimationConfig() .setRadius(16f) // 单位 px;若设计稿为 dp,需 × density 换算 .setColor(0xFF000000); ViewAnimationListener listener = new ViewAnimationListener() { @Override public void onAnimationStart(int animMode) {} @Override public void onAnimationEnd(int animMode) {} @Override public void onAnimationCancel(int animMode) {} }; ActivityOptions options = ViewAnimationManager.makeViewAnimationOptions( sourceView, config.toBundle(), listener); if (options != null) { startActivity(intent, options.toBundle()); } else { startActivity(intent); } ``` > **圆角自动采集**:View 源的圆角半径由框架从 View 的 `Outline` 自动获取(单位 px),bundle 中 `setRadius` 不传或传 0 时优先用 Outline 值。复杂自定义圆角(非 Outline)需显式传 `setRadius`(同样 px)。 ### 2. Bitmap 源启动动画 当 View 自动截图不可靠,或源内容来自缓存缩略图时,可使用 Bitmap 源重载: ```java import android.graphics.Bitmap; import android.graphics.Rect; import android.os.Bundle; Bitmap thumbnail = obtainThumbnail(); Rect bounds = obtainBounds(sourceView); float radius = 16f; // 单位 px;若设计稿为 dp,需 × density 换算 ActivityOptions options = ViewAnimationManager.makeViewAnimationOptions( thumbnail, bounds, radius, new Bundle(), listener); if (options != null) { startActivity(intent, options.toBundle()); } else { startActivity(intent); } ``` Bitmap 源要求: - `thumbnail` 必传且不能被 `recycle` - `bounds` 必传 - `radius` 不能为负数,**单位 px**(与 View 源经 Outline 自动采集的圆角同语义同单位;传 dp 值会被当 px 消费,导致 OPEN/CLOSE 圆角不一致跳闪,应用需 `dp × density` 换算后传入) - Bitmap 源的 `thumbnail` / `bounds` / `radius` 是显式参数,优先级高于 Bundle 中的同名配置 ### 3. 返回前刷新配置 返回前可调用 `overrideViewAnimationTransition` 刷新返回位置、返回动画类型或其他支持的参数: ```java Rect updatedBounds = obtainBounds(sourceView); ViewAnimationConfig config = new ViewAnimationConfig() .setBounds(updatedBounds) .setBackAnimType(ViewAnimationType.VIEW_ANIM_FADE_OUT); ViewAnimationManager.overrideViewAnimationTransition(this, config.toBundle()); finish(); ``` 如果返回目标 View 已经不可见或位置变化过大,建议使用 `VIEW_ANIM_FOLLOW_SYSTEM`、`VIEW_ANIM_FADE_OUT` 或 `VIEW_ANIM_EXPLODE_OUT` 作为更稳妥的返回 / 兜底策略。 > **override 入参**:`overrideViewAnimationTransition(activity, bundle)` 的 `activity` 是**即将退出 / 返回的目标页面 Activity**(返回发起方,即调用方 `this`),必须在 `onCreate` 之后、`finish` 之前调用。 ### 滚动入视(demo 内置能力) 设置页第 6 个开关「滚动入视」,默认开启。半遮卡片(被列表视口顶/底/左/右裁切)启动 一镜到底后,返回前把列表最小滚动到卡片完全可见,返回动画落点不被遮挡: - 滚动唯一入口在返回时(关闭动画启动前、详情页仍覆盖源页的时刻执行,过程不可见), 快速返回取消 OPEN 的路径由同一入口兜底;点击侧仅登记返回目标,不在动画回调里滚动 - 返回前用滚动后的新窗口 bounds 做 `overrideViewAnimationTransition`,且 override 先于 `finish()` 调用——晚于关闭动画启动会被 KIT `position_changed` 守卫取消整场动画(表现为返回动画一帧消失) - 与「使用Bitmap接口」互斥:Bitmap 源走显式 bounds 参数,无源 View 语义,不参与滚动入视 - 仅对点击瞬间被裁切的卡片生效(delta==0 零开销直通);纵向瀑布流与横向网格通用 ### 4. 强制结束当前动画 ```java ViewAnimationManager.cancelCurrentViewAnimation(); ``` 在动画未结束时强制结束并复位到起始状态(如退出应用、切换根任务)。 --- ## 公共接口 ### ViewAnimationManager SDK 主入口(静态方法),所有方法线程安全,建议在主线程调用。 | 方法 | 说明 | |------|------| | `isViewAnimationSupported()` | 查询当前设备是否支持一镜到底能力,返回协议版本(>0 支持,0 不支持) | | `makeViewAnimationOptions(View, Bundle, ViewAnimationListener)` | View 源:基于源 View 构造动画 ActivityOptions | | `makeViewAnimationOptions(Bitmap, Rect, float, Bundle, ViewAnimationListener)` | Bitmap 源:基于 Bitmap + bounds + radius 构造 | | `overrideViewAnimationTransition(Activity, Bundle)` | 返回前刷新返回位置 / 动画类型等配置 | | `cancelCurrentViewAnimation()` | 强制结束当前动画 | ### ViewAnimationListener 动画状态回调。 | 方法 | 说明 | |------|------| | `onAnimationStart(int animMode)` | 动画开始 | | `onAnimationEnd(int animMode)` | 动画正常结束 | | `onAnimationCancel(int animMode)` | 动画被取消(如被新动画打断) | `animMode` 取值: | 常量 | 值 | 说明 | |---|---|---| | `ViewAnimationManager.ANIM_MODE_OPEN` | `0` | 打开动画 | | `ViewAnimationManager.ANIM_MODE_BACK` | `1` | 返回动画 | 公开 listener 只回调动画方向 / 生命周期,不回调实际动画类型。 ### ViewAnimationConfig 链式调用 API 构造基础参数,通过 `toBundle()` 序列化为 `Bundle`。 | 属性 | Setter | 说明 | |---|---|---| | `backAnimType` | `setBackAnimType(int)` | 返回动画类型 | | `color` | `setColor(int)` | 背景填充色,ARGB 色值;显式传入时必须等于返回落点(列表页)的底色 | | `radius` | `setRadius(float)` | 圆角半径,单位 px;View 源可由框架从 Outline 自动获取,Bitmap 源必须显式传入,且与卡片实际圆角同单位同值(设计稿为 dp 时需 `dp × density` 换算为 px) | | `bounds` | `setBounds(Rect)` | View 显示区域;显式传入前应先判断 bounds 坐标系 | --- ## 参数定义 ### Bundle 基础参数 | Key | 类型 | 默认值 | 说明 | |---|---|---|---| | `EXTRA_BACK_ANIM_TYPE` | `int` | `VIEW_ANIM_DEFAULT` | 返回动画类型 | | `EXTRA_COLOR` | `int` | `0` | 背景填充色,ARGB 色值 | | `EXTRA_RADIUS` | `float` | `0` | 圆角半径,单位 px | | `EXTRA_BOUNDS` | `Rect` | `null` | View 显示区域;缺省按屏幕坐标,若能力声明为 `WINDOW` 则使用窗口坐标 | ### Bundle 扩展参数 扩展参数必须先通过 `getViewAnimationExtras()` 判断支持情况。 | Key | 类型 | 默认值 | 说明 | |---|---|---|---| | `EXTRA_SHADOW` | `float` | `0` | 阴影效果强度 | | `EXTRA_BACKGROUND_SCALE` | `float` | `1.0` | 背景层缩放比例 | | `EXTRA_DEPTH` | `float` | `0` | 景深效果强度 | | `EXTRA_BLUR` | `boolean` | `false` | 背景模糊 | | `EXTRA_PRESS_EFFECT` | `boolean` | `false` | 是否启用按压态反馈 | | `EXTRA_VIEW_VISIBLE` | `boolean` | `false` | 动画过程中原 View 是否可见 | | `EXTRA_VIEW_WITH_ALPHA` | `boolean` | `false` | View 是否自带透明度 | | `EXTRA_FALLBACK_ANIM_TYPE` | `int` | 设备默认 | 异常兜底动画类型 | | `EXTRA_COVER_RATIO` | `float[2]` | `{0, 0}` | 页面上下遮挡区域占比 | | `EXTRA_VISIBLE_RECT` | `Rect` | `null` | 卡片可见区域裁切范围 | | `EXTRA_BITMAP` | `Bitmap` | `null` | 应用提供的动画源 Bitmap | | `EXTRA_EDGE_FILL_MODE` | `int` | `0` | 截图边缘填充策略:0=默认不处理,1=边缘像素填充 | `EXTRA_BITMAP` 约束: - View 源 `makeViewAnimationOptions` 可接受,用于覆盖设备侧对 View 的自动截图 - Bitmap 源重载不接受,因为 Bitmap 已通过 `thumbnail` 显式传入 - `overrideViewAnimationTransition` 可接受,用于返回前刷新 Bitmap - 仅在 `getViewAnimationExtras()` 返回支持时写入 ### 返回动画类型 | 常量 | 值 | 说明 | |---|---|---| | `VIEW_ANIM_DEFAULT` | `0` | 默认一镜到底动画 | | `VIEW_ANIM_NONE` | `1` | 无动画 | | `VIEW_ANIM_FOLLOW_SYSTEM` | `2` | 跟随系统 / 应用原有返回动画,Kit 不接管返回动效 | | `VIEW_ANIM_FADE_OUT` | `3` | Alpha 淡出 | | `VIEW_ANIM_EXPLODE_OUT` | `4` | 中心收缩退出 | `VIEW_ANIM_EXPLODE_OUT` 适合作为 `EXTRA_FALLBACK_ANIM_TYPE` 的推荐值,在截图丢失、目标丢失或位置无法可靠获取时提供较自然的兜底过渡。 --- ## Bounds 坐标系 应用显式传入 `EXTRA_BOUNDS` 或 Bitmap 源 `bounds` 前,先读取: ```java Map extras = ViewAnimationManager.getViewAnimationExtras(); String boundsMeta = extras != null ? extras.get(ViewAnimationExtras.EXTRA_BOUNDS) : null; String coordinateToken = parseBoundsCoordinateToken(boundsMeta); ``` 解析规则: | metadata value | 坐标系 | 采集方式 | |---|---|---| | `null` / 未返回 | `SCREEN` | `View.getLocationOnScreen(...)` / `getBoundsOnScreen(...)` | | `rect` | `SCREEN` | `View.getLocationOnScreen(...)` / `getBoundsOnScreen(...)` | | rect|View bounds... | `SCREEN` | `View.getLocationOnScreen(...)` / `getBoundsOnScreen(...)` | | rect|SCREEN|View bounds... | `SCREEN` | `View.getLocationOnScreen(...)` / `getBoundsOnScreen(...)` | | rect|WINDOW|View bounds... | `WINDOW` | `View.getLocationInWindow(...)` | ```java static String parseBoundsCoordinateToken(String value) { if (value == null || value.trim().isEmpty()) { return "SCREEN"; } String[] parts = value.split("\\|", -1); if (parts.length >= 2) { String token = parts[1].trim().toUpperCase(Locale.ROOT); if ("SCREEN".equals(token) || "WINDOW".equals(token)) { return token; } } return value.toUpperCase(Locale.ROOT).contains("WINDOW") ? "WINDOW" : "SCREEN"; } ``` 约束: - `sharedView.bounds` 是 Map key;value 不写成 `sharedView.bounds|WINDOW|...` - 第二段 token 优先;`contains("WINDOW")` 只作为老描述兜底 - 应用不得根据设备型号或系统版本硬编码坐标系 - 返回前刷新 bounds 时,也使用同一套坐标系判断 --- ## backAnimType / fallbackAnimType 支持集合 设备侧通过 `getViewAnimationExtras()` 是否返回对应 key 来声明当前设备是否支持应用显式配置该参数。 判断规则: | 返回情况 | 应用侧判断 | |---|---| | 未返回 `EXTRA_BACK_ANIM_TYPE` | 不假定 `EXTRA_BACK_ANIM_TYPE` 会生效,使用设备默认返回动画策略 | | 未返回 `EXTRA_FALLBACK_ANIM_TYPE` | 不假定 `EXTRA_FALLBACK_ANIM_TYPE` 会生效,使用设备默认异常兜底策略 | | 返回 `int` | 参数可写,但精确支持集合未知 | | 返回 int|VIEW_ANIM_DEFAULT,VIEW_ANIM_NONE,...|... | 第二段是机器可读支持集合 | 推荐 value 示例: ```text EXTRA_BACK_ANIM_TYPE -> int|VIEW_ANIM_DEFAULT,VIEW_ANIM_NONE,VIEW_ANIM_FOLLOW_SYSTEM,VIEW_ANIM_FADE_OUT,VIEW_ANIM_EXPLODE_OUT|Back animation type EXTRA_FALLBACK_ANIM_TYPE -> int|VIEW_ANIM_DEFAULT,VIEW_ANIM_NONE|Fallback animation type ``` `backAnimType` 与 `fallbackAnimType` 的支持集合可以不同,应用不能因为前者支持某个 `VIEW_ANIM_*` 就推断后者也支持同一类型。第二段解析失败时,只能认为"参数可写、精确集合未知"。 --- ## 异常与降级 ### 返回 null `makeViewAnimationOptions` 可能返回 `null`。常见原因: - 当前设备不支持 - source View 未 attach 或已 detach - source View 无法获取有效位置 - Bitmap 已回收 - 设备侧运行时暂不可用 应用处理方式: ```java ActivityOptions options = ViewAnimationManager.makeViewAnimationOptions(view, bundle, listener); if (options != null) { startActivity(intent, options.toBundle()); } else { startActivity(intent); } ``` ### IllegalArgumentException 应用侧明显传错参数时可能抛出 `IllegalArgumentException`: | 失败原因 | 说明 | |---|---| | View 为 null | View 源接口中 `view` 参数为 null | | Bitmap 为 null | Bitmap 源接口中 `thumbnail` 参数为 null | | bounds 为 null | Bitmap 源接口中 `bounds` 参数为 null | | radius 为负 | 圆角半径为负数 | 这些属于调用代码问题,应在调用前检查。 ### 建议降级策略 - 设备不支持:普通 `startActivity` - 构造失败:普通 `startActivity` - 返回目标丢失:设置 `EXTRA_FALLBACK_ANIM_TYPE` - 返回位置变化过大:使用 `VIEW_ANIM_FOLLOW_SYSTEM` 或 `VIEW_ANIM_FADE_OUT` - View 自动截图不可靠:优先使用 `EXTRA_BITMAP` 或 Bitmap 源重载 --- ## Demo 使用说明 1. 该 demo 使用 `app/src/main/assets/images/` 下的内置示例图片模拟内容,不需要读取相册或申请照片权限; 2. 滑动列表用于模拟位置变化、列表滑动、view 遮挡等场景; 3. 跳转逻辑在 `presenter/` 中(`HomePresenter`)。 --- ## 常见问题 **Q1: 什么时候用 View 源,什么时候用 Bitmap 源?** A: View 稳定、已 attach、可截图时优先用 View 源;View 可能被回收、截图不可靠或动画源来自缓存缩略图时使用 Bitmap 源。 **Q2: getViewAnimationExtras() 未返回某个 key,还能写入吗?** A: 不建议。未返回表示当前设备不声明支持该参数,写入后不应假定生效。 **Q3: getViewAnimationExtras() 返回 `int`,但没有列出支持集合怎么办?** A: 表示参数可写,但精确支持集合未知。应用可以写公开合法值,同时准备默认策略。 **Q4: public ViewAnimationListener 能拿到实际执行的动画类型吗?** A: 不能。公开 listener 只携带 `animMode`,用于区分打开 / 返回方向和生命周期。 **Q5: Bitmap 源可以同时把 `EXTRA_BITMAP` 放进 Bundle 吗?** A: 不建议,也不需要。Bitmap 源已经通过 `thumbnail` 显式传入 Bitmap,Bundle 中的 `EXTRA_BITMAP` 不会作为 Bitmap 源输入使用。 **Q6: 返回时卡片位置变了怎么办?** A: 在 `finish()` 前调用 `overrideViewAnimationTransition` 刷新 `EXTRA_BOUNDS`;如果位置变化过大,建议切到 `VIEW_ANIM_FOLLOW_SYSTEM`、`VIEW_ANIM_FADE_OUT` 或 `VIEW_ANIM_EXPLODE_OUT`。 **Q7: 动画起点或返回落点和实际 View 对不上,如何排查 bounds?** A: 优先确认 `EXTRA_BOUNDS` 使用的坐标系是否和当前设备声明一致。应用不要按经验固定使用屏幕坐标或窗口坐标,应先查询: ```java Map extras = ViewAnimationManager.getViewAnimationExtras(); String boundsMeta = extras != null ? extras.get(ViewAnimationExtras.EXTRA_BOUNDS) : null; ``` 判断规则: - `boundsMeta` 为 `rect|WINDOW|...`:使用窗口坐标,调用 `View.getLocationInWindow(...)` 计算 bounds。 - `boundsMeta` 为 `null`、`rect`、`rect|...` 或 `rect|SCREEN|...`:使用屏幕坐标,调用 `View.getLocationOnScreen(...)` 或 `getBoundsOnScreen(...)` 计算 bounds。 - 第二段 token 优先;不要通过设备型号、系统版本或自然语言描述硬编码判断。 如果启动和返回都需要传 bounds,两处必须使用同一套坐标系判断;返回前通过 `overrideViewAnimationTransition` 刷新 bounds 时也一样。 **Q8: 显式传 `color` 时应该传什么色?返回动画末段出现色圈是什么原因?** A: `color` 是动画 mask(卡片外扩底板)的填充色,**只在返回动画收尾回弹段可见**——打开阶段该色层被卡片截图完全遮蔽,传错色在打开时看不出来。选色规则: > **必须传「动画落点处的背景色」——即返回时列表页占屏大头的底色(通常是列表 RecyclerView / 页面根节点的 `android:background` 实际值);不是目标详情页的背景色,也不是被点击卡片的颜色。** 原因:返回动画收尾时 mask 落回列表页,mask 边界内侧是传的 `color`、外侧直接对照列表底色,两者不一致即出现一圈色差(白 mask 落在 `#F5F5F5` 灰底列表上是典型 case)。 补充规则: - 没有稳定底色的页面(动态封面 + 遮罩、渐变背景)**不传**,由系统自动取色兜底;不要估算"合成色"。 - 「看起来是白」的列表不一定是纯白,`#F5F5F5` / `#FAFAFA` / `#F7F8FA` 视觉相近但都会产生色圈,一律取布局实际值。 - 若返回动画**中段**出现整屏异色但收尾正常,那是详情页内容(头图等)随截图收缩,与 `color` 无关。 **Q9: `ViewAnimationConfig` 没有提供 remove / clear 类接口,想剔除已设置的参数(如 `bounds`)怎么办?** A: `ViewAnimationConfig` 只负责链式构造,`toBundle()` 的产物就是普通 `Bundle`,KIT 与设备侧都按最终 Bundle 内容消费。因此不需要 Config 层的 remove 接口,在 `toBundle()` 之后按 key 剔除即可: ```java import android.os.Bundle; import com.itgsa.animation.sharedview.ViewAnimationExtras; ViewAnimationConfig config = new ViewAnimationConfig() .setRadius(16f) .setBounds(srcRectOnScreen) // 构造时已设置 bounds .setBackAnimType(ViewAnimationType.VIEW_ANIM_FOLLOW_SYSTEM); // toBundle() 返回普通 Bundle,可直接按 key 剔除 Bundle bundle = config.toBundle(); bundle.remove(ViewAnimationExtras.EXTRA_BOUNDS); // 剔除后由框架对源 View 自动定位 ActivityOptions options = ViewAnimationManager.makeViewAnimationOptions( sourceView, bundle, listener); if (options != null) { startActivity(intent, options.toBundle()); } else { startActivity(intent); } ``` 约束: - 剔除必须在 `toBundle()` 之后、传入 `makeViewAnimationOptions` / `overrideViewAnimationTransition` 之前完成。`ActivityOptions` 一经创建,内部配置无法修改——若已创建且需要剔除,应先 remove 再重新调用 `makeViewAnimationOptions` 重建,丢弃旧 options。 - 该方式对 `ViewAnimationExtras` 中所有 key 同样适用(如 `EXTRA_COLOR`、`EXTRA_BACK_ANIM_TYPE` 及扩展参数)。 - Bitmap 源的 `bounds` 是显式必传参数,剔除 Bundle 中的 `EXTRA_BOUNDS` 不影响显式参数本身。 - `overrideViewAnimationTransition` 的入参 Bundle 只表达"设置 / 覆盖某 key",无法表达"删除已生效的 key"——不传该 key 是"不更新",不是"清除"。