用途
该资料面向使用 Kotlin 与 Jetpack Compose 开发全屏视频播放页的开发者,给出一套手势层与控件层的组合结构及配套的排错清单,帮助避免点击区域失效、计时器重置错误、进度条回弹、画中画误触等常见问题。
主要能力与结构
全屏视频页被组织为同一个容器内的三个同级层,顺序固定:
- 视频画面层
VideoSurface,始终存在; - 手势层,由两个
weight(1f)的SeekZone组成,铺满整个画面,中间不留死区,保证每次点击都落在某个区域内; - 控件层,用
Crossfade(chromeVisible)控制显隐。隐藏时控件不参与组合,点击会穿透到下方手势层;显示时控件的按钮位于最上层,自行接收点击。
画中画模式(!isInPipMode)下手势层和控件层都不渲染,只有视频画面层保留,避免在缩略窗口内误触。
使用方式与关键实现要点
资料以代码片段说明实现方式,主要涉及:
- 双击快进/快退与单击切换控件:每个手势区都要同时配置
onTap与onDoubleTap。若某个区只有双击而没有单击,该半屏点击就不会切换控件。 - 点击反馈:
detectTapGestures本身不提供按压反馈,需手动通过interactionSource与ripple()驱动涟漪;释放时须使用与按下相同的PressInteraction.Press实例,否则指示不会清除;emit是挂起函数,需要协程作用域;涟漪受修饰符链中clip约束,因此对区域内侧圆角裁剪才能呈现圆角半屏效果。 - 自动隐藏计时器:使用
LaunchedEffect,以控件可见性和拖动状态作为键。以可见性为键可在每次切换时重新计时;以拖动状态为键可在拖动开始时取消、结束时重新启动,从而给用户完整窗口期。新增需要保持控件显示的行为时,应作为新的键加入,而非在函数体内加分支。 - 进度条拖动:拖动期间用
isSliding标志阻止播放器位置更新覆盖拖动值,在onValueChange中置位、onValueChangeFinished中清除并派发一次跳转,避免每帧跳转。 - 跳转命令统一:手势与按钮应派发同一个引擎自带的前进/后退命令,而非在 UI 层计算“当前位置 ± N”;旁边标注的步长需要与引擎配置一致。
- 画中画状态:须以可触发重组的 Compose 状态读取,不能在普通变量中采样一次;有模式回调的平台订阅回调,没有的平台用常量 false。
- 退出全屏恢复:进入全屏通常涉及锁定方向、隐藏系统栏、保持屏幕常亮,退出时需全部还原;保存原始方向必须在强制横屏之前;并检查恢复逻辑只写一次,避免
when分支成为死代码。
验证方法
资料给出 grep 命令核对代码:
grep -rn --include="*.kt" "onDoubleTap" .每个跳转区应各有一处;grep -rn --include="*.kt" -B 8 "delay(" . | grep "LaunchedEffect"每个计时器一处,并建议加大上下文行数以覆盖位于 else 分支的 delay;grep -rn --include="*.kt" "PressInteraction.Press(" .每处都须绑定局部变量并在下方用同一实例释放;grep -rn --include="*.kt" "rememberIsInPipMode\|isInPictureInPictureMode" .一处声明、每平台一份实现、每个绘制控件覆盖视频的页面一处读取。
手动验证顺序:点击右半屏应出现控件;双击左右半屏应跳转并出现以手指为中心的淡出涟漪;开始拖动并静止超过自动隐藏时长,控件应保持,松开后应再保持一个完整窗口期;缩为浮窗后应无控件、无手势,恢复后正常。最后离开页面,确认设备方向与系统栏恢复原状。
适用场景与限制
适用于基于 Compose 实现全屏视频播放交互的 Android 开发者,可作为实现与代码审查的参考。资料未提供安装方式、依赖版本、运行环境要求、发布形式或收费信息;代码片段由资料作者标注为“adapted”,使用前需结合自身项目调整。