手记 底栏

从不离开的导航栏

Yoin 里所有页面都在动,唯独底部的浮动栏一直停在原处。为了不让它动哪怕一像素,我们在底下折腾了整个应用里最复杂的一套窗口与布局工程。

大多数应用的底栏只是排着几个标签的固定盒子。做 Yoin 时,我把导航和迷你播放器揉进了一根浮动栏里:首页和资料库守在两边,正在播的歌夹在中间。点一下中央胶囊,播放页向上撑开;点开专辑,详情页从上方滑入;侧滑返回把上层页面拉走,这根底栏依然稳稳停在原处。

想让它看起来轻轻松松地待在原地,底下却得解开一连串相当拧巴的布局与窗口难题。

三个按钮的宽度守恒

最初我直接用了 Material 3 Expressive 的官方按钮组组件。按住任何一个按钮,它会向外鼓起,旁边的按钮跟着收窄让位,整排按钮摸起来很有弹性。

按住底栏任意位置。点中的按钮向外变宽,多出来的宽度由中间的曲目胶囊吃掉。切到“第一版”,能看到我最初写反符号时越按越小的样子。

结果这个官方组件在折叠屏上崩了。在 Pixel Fold 内屏展开正在播放页时,转场中途传进去的极端尺寸让组件算出了负数宽度,直接抛了 Android 布局异常。

没办法,只能甩开官方组件自己重写整条底栏,亲手算按压形变。第一版我不小心把正负号写反了,按钮一按反而往里缩,跟 Expressive 的手感完全拧着来;改对之后,按左右导航键时按钮向外撑开,多出的宽度由中间的曲目胶囊吸收;按住中央胶囊,两边按钮就一块儿往外退。

甩掉现成组件、改成手算每个槽位的宽度之后,不管底栏变成什么样,总宽和间距都是干净利落的纯函数。

导航的四种形态

这根底栏其实一直在换造型。没播歌的时候,中间的胶囊完全收起,首页和资料库向中间拉宽,露出文字标签;歌一响,曲目胶囊就挤进它们中间;点进专辑页,首页和资料库收成 0,原地长出这张专辑的“播放”分体按钮;到了平板的双列画面上,导航、曲目胶囊和专辑按钮干脆一字排开。

每种造型说穿了只是一组槽位宽度。切换时由同一个空间弹簧带着所有槽位一起过渡,哪怕半路划回来,布局也绝不会跳一下。

最早其实还有第五种造型:没播歌时中间也留着空胶囊,点进去是一整页空荡荡的“未在播放”。后来干脆把空槽折起来,这个多余的空页面也就一起砍掉了。

曲目切入的方向感

切到下一首歌时,上一首的歌名往左快速闪开,新歌名从右边顺着空间弹簧推入,停稳前带一点轻微的回弹。在 Yoin 的其他地方,我们也保留了这种简单的骄傲的「安卓感」。

点一下“下一首”。旧歌名从左边利落退场,新歌名乘着空间弹簧从右侧滑入落定。

两个独立窗口,同一根底栏

在手机竖屏这种单列画面下,为了用上 Android 14+ 原生的跨 Activity 预测性返回——也就是手指往回滑时,底下能直接露出真实的首页——专辑、艺术家和播放列表详情页其实都跑在各自独立的窗口里。问题是,Android 的两个窗口之间根本没法共享任何视图,连底栏也不行。

我们的障眼法,是在两个窗口完全相同的屏幕坐标上,各画一根逐像素对齐的底栏。点开专辑的那一刻,底下窗口的底栏原地开始变形,上层专辑窗口同时淡入,由另一根位置分毫不差的底栏接手。侧滑返回时,只有上层页面跟着手指往边上缩,底栏钉在原地,随着你的手势倒着变回原来的模样。

拖动滑块试试系统返回手势。上层页面往右缩小退开,露出底下的首页;底栏跟着手势变形,但位置一像素都不晃。拖过三分之一松手就退回上一页,不到三分之一则会弹回来。

手指还在屏幕上时,所有形变都跟手指位移 1:1 咬合,不加一点滤波或延迟;直到你松手的那一瞬间,才交给弹簧收尾。

要是两根底栏都带阴影,窗口交接时影子叠在一起立刻就会穿帮。所以我们干脆去掉了阴影,改用底下的半调网点把底栏和滚过的内容隔开。

宽屏下的同窗合体

到了平板或展开的折叠屏上,详情页不再另开新窗口,而是直接从同一个窗口的右边滑进来。这时底栏会横跨左右两栏:左边管首页和资料库,中间是能伸缩的正在播放胶囊,右边直接长出这张专辑的播放与随机播放按钮。关掉右栏时,右边那组按钮又顺着同一套槽位插值,折回胶囊里去。

隐形的基准线

折腾这么多,其实只是为了让你在听歌时根本不用分神去想底栏在哪儿。无论页面如何推入、分列或撤回,它始终安静地停在拇指下方。但每次当你通过不同方式返回时,你一定会感受到我们用心的设计。

继续阅读

封闭测试

加入封闭测试。

Yoin 0.5 正在 Google Play 上封闭测试,加入大约需要两分钟。