在本文中,我们将创建一个玻璃形态设计风格的底部导航栏。我们将使用 Chris Banes 的新 Haze 库来完成这项工作。这个库使我们能够轻松地模糊 Composable 的背景,这意味着元素后面的任何内容都将可见,但会模糊。
最终,我们将构建一个如下所示的底部导航栏:
准备工作
首先,让我们创建一个基本布局来应用我们的毛玻璃效果底部导航栏。我们将使用 Scaffold
来在屏幕上放置主要内容和底部导航栏,如下所示:
注意,我们将 Scaffold
提供的padding作为 contentPadding
使用,而不是作为padding修饰符。这是为了让内容占据整个屏幕,底部导航(或顶部应用栏,如果存在)将覆盖在内容之上。 但当我们到达列表的末端时,会为导航栏留出足够的空间。
关于底部导航栏的实现,让我们创建一些要显示的示例标签。
BottomBarTab
密封类定义了一些包含基本信息的标签。每个标签都有自己的标题、图标和选中时的独特颜色。
有了这些示例标签,让我们模拟它们之间的切换。为此,我们将使用一个在点击标签时改变的整数状态。
现在让我们把这些都整合在一起,在 BottomBarTabs
中渲染底部导航栏:
在这里,我们遍历所有标签,并在一个Row中使用我们之前定义的标题和图标渲染它们。我们还为切换标签添加了一些基本动画。 有一个透明度动画使非活动标签变暗,以及一个缩放动画,为标签选择添加一点动感。
现在我们已经完成了设置,让我们继续创建玻璃效果。
Haze库设置
在项目中包含 Haze 库,并确保版本是 0.4.1 或更高。
要实现模糊效果,我们必须告诉 Haze 哪个 Composable 需要模糊,以及哪个 Composable 引起和定位该模糊。我们分别使用 haze
和 hazeChild
修饰符来完成这个,并使用 hazeState
将它们连接起来。
在我们的例子中,我们想要模糊的 Composable 是 LazyColumn
。所以让我们给它添加 haze 修饰符。
在 haze
修饰符中,传入 hazeState
后,我还传入了一些对我来说效果不错的值。但你可以进一步实验这些值来创造独特的效果。
至于 hazeChild
修饰符,我们将把它应用到底部导航栏上:
我们应用修饰符并传入与 LazyColumn
中相同的 hazeState。这给我们带来了以下结果。
这看起来不错,但我们想要模糊区域有圆形的形状。幸运的是,Haze 库允许我们传入一个形状来定义要模糊的区域边界。
现在我们为底部导航栏有了一个漂亮的圆形模糊形状。
细节完善
目前我们的效果看起来还可以,但我们可以添加一些更多的细节来让它更上一层楼。第一个改变很简单,但它会大大改善我们底部导航栏的外观。
让我们在模糊区域周围添加一个边框。
这样做给元素带来了一定的秩序感。为了配合玻璃美感,我们不是简单地添加一个实心边框,而是使用一个渐变来模拟光线落在元素顶部的效果。
接下来,我们将在选择标签时添加一些动画。
首先让我们实现一些动画值。我们将为选定的标签索引和我们之前设置的标签颜色添加动画。
有了这些值,让我们添加一个会在当前选中标签上动画的光晕,并将其光晕颜色动画到相应的标签颜色。
我们使用一个 Canvas
来实现这个效果,在其中绘制一个圆,其中心是当前标签的中心。这个 Canvas 的大小与底部导航栏完全相同,所以我们可以平均分割它,并使用 animatedSelectedTabIndex
为选中的标签添加动画。 圆的颜色是 animatedColor
的一个低透明度变体。
最后,我们模糊整个画布并裁剪它,这样模糊就不会落在下面的内容上。
最后一个细节是在底部导航栏的底部添加一个光泽。这模拟了指示器光晕在我们的玻璃状 Composable 底部边缘的反射。我们也将使用一个裁剪过的 Canvas 来实现这个效果,但不需要模糊。
在 Canvas
中,我们创建一个围绕底部导航栏的圆角矩形路径,然后测量其长度。
一旦我们定义了路径,我们就用 animatedColor
的渐变来绘制它。这个渐变的位置从当前选中标签的左边缘开始,到右边缘结束。这是通过 animatedSelectedTabIndex
计算的,所以它会随着光晕平滑地动画。 最后,我们添加一个 dashPathEffect
,它只绘制路径的下半部分。
这样,我们就得到了这个令人愉悦的底部导航栏动画。
完整代码可在这里找到。
这只是我们用这种玻璃形态UI能实现的效果的开始。我希望这篇文章能教会你并激发你走得更远。
感谢阅读,祝你好运!