该技能围绕在 Compose 中使用 backdrop 库实现折射式“液态玻璃”表面的实践展开,按资料内容可分为用途、机制、常见陷阱、验证方式与相关主题几部分。
用途:帮助开发者理解并修正液态玻璃表面在 Compose 中的三类高频问题,即边缘高光(rim highlight)、backdrop 源与表面(source/surface)关系、以及效果栈(effect stack)。资料指出效果栈是按视觉逐项调整的滤镜列表,三者按引发麻烦的程度排序。
机制:backdrop 库把效果拆为两部分。source 修饰符把某个 composable 的渲染记录进图形层;表面上的 draw 修饰符对这段记录进行采样,经过效果栈处理,并把结果绘制在自己背后。示例中效果栈依次包含 vibrancy、colorControls(亮度 0.05、对比度 1、饱和度 1.5)、blur 和 lens;lens 的折射高度低于内切圆半径(minDimension / 2),以免上下折射在中轴相接形成宽药丸形上的暗缝。onDrawSurface 可在背景变亮时压暗表面,防止明亮画面把玻璃冲成白色。
陷阱一,边缘高光默认样式是方向性的,并非描边。默认 shader 以点积计算某点边缘法线与固定光方向的对齐程度,再取 falloff 次幂决定亮度。在胶囊形状上整条直边共享同一轴对齐法线,两条长边会同时均匀发亮,看起来像边缘高光;在圆形上法线扫过所有方向,亮度按 |cos(θ − angle)| 变化,只有两个相对点达到最亮,相隔 90° 的两点为 0,48 dp 圆形按钮上几乎看不到高光。资料给出两种独立手段:改用库中均匀样式(只有颜色和混合模式,无角度、无 falloff),或加宽边缘(width 决定 strokeWidth = ceil(width.toPx()) * 2,默认 0.5 dp 是库绘制的最细描边,Highlight(width = 1.dp) 能补回足够的像素)。资料说明在其审计的代码树中,凡使用调节手段的地方都采用第二种,以与旁边的胶囊控件保持风格一致。资料注明在 2.0.x 版本中,默认样式为白色、alpha 0.5、加法混合、45°、falloff 1;均匀样式为白色、alpha 0.38、加法混合。
陷阱二,backdrop 源必须是玻璃的兄弟节点,绝不能是父节点。源记录自身绘制内容,表面在绘制时读取该记录,若表面位于源内部,记录就依赖自身输出,形成循环,在绘制阶段会中断 shader。示例中源是内容列,玻璃按钮通过 align() 作为兄弟节点放置。另外,源若什么都不画,玻璃就没有可折射的内容;在空白区域上输出只剩表面遮罩,扁平页面上控件会渲染成灰色圆片。资料记录了一次完整来回:引入玻璃、因该理由移除改用普通图标按钮、页面有了值得折射的底之后又恢复。底通常是携带页面颜色与环境色调的 matchParentSize() 盒子,而非大部分透明的内容列。内容确有绘制时(如首项为全宽图片头的列表)内容列才是合适的源,其条带由 Spacer 预留。玻璃还需浮在底之上,而不是随滚动列滚走。
陷阱三,默认色调假设屏幕处于强制深色环境。这类控件常先在强制深色子树中构建,白色图标叠在深色画面上,Color.White 是合理的;但在跟随主题的页面上,浅色主题下会出现白底白图标,而设置不到位时换入的扁平回退也是浅色,两种材质下图标都消失。应改为传入配色方案的 on-surface 令牌;资料指出捐赠页面因处于强制深色子树才没有遇到此问题。
诊断方法:当一个玻璃控件正常而另一个不正常时,先交换二者位置。若效果跟随控件,原因在控件自身几何(即边缘高光陷阱);若跟随位置,原因在源在该处记录的内容。资料提到曾进行四次重建,先尝试了加宽按钮至 96 dp(更长边缘能覆盖更多扫掠范围,边缘高光确实出现,但只是症状而非修复)、缩小透镜半径、在整尺寸盒子与内容列之间移动源、以低 alpha 绘制画面。
手势:按压手势必须只观察不消费。玻璃按下时会放大并绘制跟随指针的辉光,同时包裹需要自行处理点击的按钮。识别器不得调用 consume(),应接受已被消费的 down(awaitFirstDown(requireUnconsumed = false)),并在变更已被消费时退出,否则会出现表面在动而内部按钮无响应。
跨平台:在编写空实现的 actual 之前,先确认库是否为其他目标提供变体。若假定 shader 重的效果只支持单平台而给桌面端写空实现,那里会留下编译通过、只渲染普通圆角盒的死代码。若构件是多平台的,抽象层就无事可做:删除 expect 类,保留 typealias 避免调用点改动,把效果移入公共代码。
验证:资料给出无需构建、直接从依赖缓存确认默认边缘高光为方向性、均匀样式不是的命令,包括用 find 定位 backdrop jar、用 unzip 列出 HighlightStyle$Default.class 与 HighlightStyle$Plain.class、用 strings 提取 shader 片段并统计 angle/falloff 出现次数,观察结果为一处声明了 angle 与 falloff 并包含点积主逻辑,另一处为 0。还给出用 grep 列出所有 layerBackdrop 调用点的命令,观察结果是每处都是玻璃的 Box/Column 兄弟节点,包括分析页面以 LazyColumn 自身为源(首项为图片头,故有绘制),按钮通过 align() 放在其后;子树中若包含同一 backdrop 的 drawBackdrop 即会崩溃,纯透明源则是灰色圆片。以及用 grep 查看各调用点使用哪种边缘高光手段,观察到六处传入 Highlight(width = 1.dp)、一个栏传入 Highlight.Default.copy(alpha = …)、没有使用均匀样式,跟随主题的一对 48 dp 返回按钮仍用裸默认值,是最先应检查的对象。最后建议把一个玻璃按钮在圆形与宽胶囊之间调整尺寸,其余不变:仅在胶囊状态下出现边缘高光即说明命中高光陷阱;若两种手段都能挽救,则几何因素得到确认。
相关主题:compose-desktop-runtime-hardening(来自渲染后端而非绘制代码的 shader 相关崩溃)与 shell-background-is-not-scheme-background(底由什么构成)。