用途
图片背景的页面靠图片获得个性,而设置、通知等扁平页面则什么都没有,看起来像另一个应用。这个共享层解决了这个问题:一个从色调到页面颜色的倾斜双色渐变,高度固定。
主要能力
- 提供
AmbientThemeGlow可组合函数,接受modifier和可空的tint颜色参数。 - 根据主题明暗对色调进行混合:浅色主题下向白色提亮(粉彩感),深色主题下加深(
rgbFactor(0.45f))。 - 使用
animateColorAsState对色调变化做 500 毫秒动画,避免硬切换闪烁。 - 在底部叠加一个 180dp 高的遮罩,使渐变尾部平滑过渡,不产生硬边。
- 高度定义为共享常量
AmbientGlowHeight = 360.dp。
使用方式
在屏幕主体的第一条语句调用,放在列表之前,无需包裹容器:
AmbientThemeGlow(tint = rememberGlowTint(currentRecord?.imageUrl))
LazyColumn(state = listState, …) { … }
滚动移出通过 graphicsLayer 在绘制阶段读取偏移实现:
AmbientThemeGlow(tint = …, modifier = Modifier.graphicsLayer {
translationY =
if (listState.firstVisibleItemIndex == 0) -listState.firstVisibleItemScrollOffset.toFloat()
else -size.height
})
适用场景
- 需要与图片背景页面保持视觉一致性的扁平页面(设置、通知等)。
- 页面顶部需要随内容滚动而移出的环境色调层。
限制与注意事项
- 不要用自定义
Box包裹屏幕:导航目标内容已组合进堆叠容器,包裹会改变子项约束,导致滚动容器测量异常。 tint为 null 时必须让渐变塌缩为页面颜色(不可见),不能替换为主题色,否则空闲应用会为未播放的记录发光。Color.Unspecified是“尚未解析”的哨兵值,不是真实颜色,否则首帧会闪现错误色调。- 尾部颜色必须指向页面实际绘制的背景色,而非
colorScheme.background,否则在带面板颜色的外壳上会出现硬接缝。 - 混合系数应属于色调来源,而非层本身;共享层用 0.45 加深,而屏幕自带的图片色调用 0.30。
- 第一个列表项必须高于该层,否则交接时会跳变。
- 顶部应用栏默认容器颜色会遮住唯一带颜色的部分,应设为透明,让栏对光晕做毛玻璃处理。
- 高度是共享常量,手写副本应导入该常量而非硬编码 360。
- 需要更多功能的屏幕应保留自己的副本:共享层只接受一个色调;自行从数据动画色调、或需要位于自身模糊源内的屏幕保留手写副本。
验证方法
# 1. 查找所有使用该层及导入高度常量的位置
grep -rn --include='*.kt' -e 'AmbientThemeGlow(' -e 'AmbientGlowHeight' . | grep -v '/build/'
# 2. 检查绘制阶段滚动移出
grep -rn -A4 --include='*.kt' 'graphicsLayer {' . | grep -v '/build/' | grep 'firstVisibleItemScrollOffset'
# 3. 检查 null 塌缩分支
grep -rn -A4 --include='*.kt' 'tint == null ->' . | grep -v '/build/'
# 4. 验证堆叠声明
JAR=$(find ~/.gradle/caches/modules-2 -name 'navigation-compose*.jar' | sort -V | tail -1) && echo "$JAR"
D=$(mktemp -d) && unzip -oq "$JAR" -d "$D" && javap -p -classpath "$D" androidx.navigation.compose.NavHostKt | grep -c 'ui\.Alignment'
目视检查:未选中任何内容时,页面应与删除该层后无法区分;选中强图片后滚动到底部再返回,观察第一个列表项离开视口时的跳变。