电表 × Android:Toolbar 的标题和三点颜色

我承认我菜,这个都要折腾。

不过其实无所谓菜不菜的问题了。

为什么要引入 Toolbar

言归正传,一般情况下,在 Android 开发中,默认的 Action Bar 其实已经够我们使用的了。但是一些比较酷炫的功能,例如折叠顶栏等,则是 Action Bar 所无法满足的,因此我们需要引入 Toolbar。

而在电表中,引入 Toolbar 的目的是为了解决主 Activity 中部分 Fragment 需要隐藏(概览、我)而其他 Fragment(发现)需要显示 Action Bar 的问题。一般做法是在需要显示的 Fragment 里调用 supportActionBar.show() 方法,而在需要隐藏的 Fragment 里调用 supportActionBar.hide() 方法。这样确实是可以的,但是会带来一个问题,通过 Bottom Navigation Bar 切换 Fragment 时,Action Bar 的显示切换并不是即时的,尤其是从隐藏切换为显示时,会明显感知到是 Action Bar 先出现,然后才是 Fragment 里的具体内容。这大大降低了用户体验。

因此,我们可以选择将主 Activity 的主题设置为 NoActionBar,然后在需要的 Fragment 里手动添加一个 Toolbar。

没完?

是的,没完。Google 在推出新版 Material Design 规范(也就是俗称的 MD2)之后,配套推出了新的 Material 库 com.google.material,其中提供的 Widget 在继承了 Android X 中 App Compat 的同时,加入了新的 Material Design 样式。

但是呢,Material 库中的 com.google.android.material.appbar.MaterialToolbar 并不严格遵守 App 设定的主题。拿电表来说,在 2.4.0 版本中加入了深色模式,对应的就应该使用 Theme.MaterialComponents.DayLight 系列中的主题,以达到切换的目的。而电表无论是浅色模式还是深色模式,colorPrimary 指定的值都是比较深的颜色,于是以其为背景的文字需要使用白色。原有的 Action Bar(基于 androidx.appcompat.widget.Toolbar)非常听话,我指定了 DarkActionBar 之后文字和三点(也就是在 Toolbar 有浮动菜单时右上角显示的按钮,官方称为 Overflow Button)颜色都是白色。然而 Material Toobar 它是个异端啊,它不接受这个 DarkActionBar 的设置啊,它非常任性啊,就觉得浅色模式的文字和三点应该是深色,深色模式的文字和三点是浅色。就很头疼啊。

怎么办?改呗。Material Toolbar 中有一个属性 titleTextColor,对应控制的就是能看见的最大的标题文字颜色(没错就是“发现”两个字),直接在 XML 里给怼成#FFFFFF完事。

麻烦的是那该死的三点。如果说 Material Toolbar 只是简单的继承之后加新样式,那就没这么多破事了。然而 Material 库中部分 Widget 的属性并不完全兼容原有 App Compat 的 Widget。就像标题中提到的,修改三点颜色成了麻烦事。原有的 androidx.appcompat.widget.Toolbar 想要实现的话,只需要在主题中(对,需要是主题而不是 XML 中的 Toolbar 本身)指定 android:textColorSecondary 的值即可。

可该死的 Material Toolbar 对这玩意儿免疫啊!我懒得再去截图了。你想象一下,左边白字,右边黑点,后边蓝底,是不是光想着都要强迫症犯了甚至打死设计师了。当我幻想着它应该能有一个属性来控制三点颜色,而麻木地用方向键浏览着 Android Studio 的智能提示列表时,我绝望了。没有!

强忍住拍键盘的冲动,我决定看一下 Material Toolbar 的源码。首先我们用饿了么开源的 UETool(强烈推荐在开发过程中尝试一下这个库,对分析应用界面非常有帮助)找到这三个点的类名 androidx.appcompat.widget.ActionMenuPresenter$OverflowMenuButton。从名字能看出来这是 ActionMenuPresenter 的一个内部类,跳转到实现后发现权限是个 private。如果为了这事用反射未免有点大动干戈。

考虑到三点属于 Toolbar 的子 View,我尝试通过 Toolbar.children 逐级找到三点。刚才在实现中看到了,这玩意儿是继承自 AppCompatImageView 的,这又是个问题了。AppCompatImageView 并没有给我们提供修改 Tint 的方法,这条路走不通了。

在自己捣鼓半天外加 STFW(Search the F**king Web)之后,总结出了两种方法。

自定义 actionOverflowButtonStyle

在当前主题中添加一项 actionOverflowButtonStyle(对于 Material Toolbar 来说应该使用那个不带 app 命名空间的),然后在样式中指定 android:tint 颜色即可。

不过有个缺点,在主题中定义的样式会对所有 Action Bar 和 Toolbar 生效,毕竟前者是基于后者的。

自定义 Toolbar 的 android:theme

下面的东西可能有点绕,主要是要区分样式 style 和主题 android:theme

在 Stack Overflow 的这篇提问中,有一个回答提到了可以重写 android:theme 来引入自定义主题以修改三点的颜色。尝试之后发现还是有一点小问题,个人稍微改进了一下。

原回答中提到的通过在主题中指定 android:textColorPrimary 来修改标题颜色,尝试之后发现这个属性同时会修改浮动菜单中的文字颜色,所以直接放弃。对于标题颜色,可以直接在 XML 设置 Material Toolbar 的 titleTextColor 属性,也可以引入样式来设置这个属性,这里不再赘述。

然后,三点的颜色,通过在主题中设置 colorOnSurface 完成。这样就完成了。

还没完

默认情况下,深色模式中 Toolbar 上文字的颜色会在白色的基础上带上 87% 的透明度,即 #DEFFFFFF,而图标的颜色会在白色的基础上带上 60% 的透明度,即 #99FFFFFF。查阅 Android 库的相关定义之后发现这个应该是通过 selector 实现的。而在上面,我们手动指定的颜色最终渲染的时候并不会有透明度,因此看起来就比较违和。

对于这个我目前没有想出来什么好的办法,可能后面试一下把官方的 selector 抄过来试试。或者可以暴力一点,在定义颜色的时候手动指定透明度。

另外就是,Toolbar 的主标题颜色是受到 ROM 影响的。在官方的 Android 10 模拟器中呈现的是有 87% 透明度的白色,而在我的小米 5(MIUI 11 based on Android 8.0)中呈现出的是白色。如果为了不同设备上的统一,其实也可以直接暴力指定 Action Bar 和 Toolbar 颜色一致。

评论

  1. cccccccc
    3 年前
    2020-12-16 9:59:23

    getOverflowIcon.setColorFilter(int)
    这样不就行了

    • Robotxm
      博主
      cccccccc
      3 年前
      2020-12-16 13:00:42

      这样倒也是一种办法,我个人比较喜欢写到 style 里

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇