前语
作为 Android 开发者,不知你是否也有这样的体会,随着项目变得越来越大,各种不同圆角的 shape,不同透明度的 color,不同巨细的阴影作用,它们使资源文件越来越多
我认= o ^ Q L ; q为造成这种问题的原因有两! d |个:一个是产品设计的不规范,整个 app 没有一致的设{ t %计风格;第二个便是开发者在开发过程中编码的不规范
Android Dev Summit ’19 有一场关于 Style 与 Theme 的讲演,它的 中文字幕视频在这儿
我为你整理了每个主题地点的方位
时刻 | 内容 |
---|---|
02:14 | Styling vs The7 0 ` m p 1 )me |
08:55 | Theme Overlay |
12:36 | Color |
17:35 | 运用及三个技巧 |
24:00 | Material 色彩 |
28:06 | Material 排版 |
30:07 | Material 形状 |
34:41 | Dark Theme |
在视频下方的谈论区,点击相应时刻即h r 4 . U f可跳转到指定内容
与之对应的有一个 Styling 的系列文章,我最近翻译成了中文
-
【译】Android StyO ! P : . D z cling 1: Themes vs Styles
-
【译】Android Styling 2: 常用主6 ~ i ) B 3 j * &题特点
-
【译】Android Styling 3: 运用主题和主题特点的优势b y ! T 8
-
【4 # % h ` d译】Android Styling 4: 主题实战
本文整理了视频与文章中的内容,介绍在开发过程中,咱们应怎么运用 theme 与 style 更优雅地管理资源文件,并供给了许多有用的技巧,在标题中找到技巧相关的查看即可
并且供给了演示 demo,作用如下
关于 recrek + –ate 黑屏闪耀的问题,请见文章底部] T = U 2 J % 20200705 更新
理解 StyZ O u Fle 与 Theme 的区别
这部分内容在视频的 02.14 处
Style 是 View 特点的调集,能够将 Style 视为 Map<View Attribute, Resource>,其中 key 为 View 的特点,value 为资源
Resource 能够为以下类型
而 Theme 则不同,它的 key 是 「主题特点」,很显然下图中的 color} – $ a ] m j E kPrimary 不是任何 View 中的特点
主题特点有点像把装备抽象为语义化的命名的变量,并把它们塞到 map 中,以便未来运用,主题特点6 C 5 l W , 3 x ) 与 View 特点很像,它们在 attr 中界说的办法以及对应的类型都是相似的,但二者仍有差异
在引证主题特点时,能够运用 ?.aJ z b F B $ttr
语法,% W v 7其中 ?代表在当时主题中搜索
主题特点带来的优势
假如咱们 app 需求支撑普通版别和 Pro 版别,它们的主色不同,咱们只需界说两个主题,装备不同的 colorPrimary。E & ? b .接着咱们需求适配深色主题,那么只需供Z p K B V A给不同的数值即可
这就好比咱们有一个 Theme 抽象类,而b y + & z / O 5 ,其中有一个抽B Y l 9 & 7 & J象特点 colorPrimary( N ? /,它有四个完成类,别离重写了 colorPrima~ / g 3 V s R z *ry 特点,这样咱们便得到了 四个变体,未来想加入新的变体,只需承继该抽象类并重写特点即可。看过 榜首A A ] { ?篇译文 的小伙伴J . p c r知道,主题的作用规模是 「树」中的一切子节点。这样咱们便很轻松地完成了更改程序主色的功用
假如要运用 St~ : l H f 8yle 完成这一功用,首先,咱们需求界说四种 Style。因为 Style 的作用规模是特定 View,因而咱们要为每个 View 均界说四套 StylH W ie
小结
简略来说,Style 与 Theme 的作用规模不同
Style 只会作用于单一的 View 中,运用时用 style
标签
Theme 会作用于「树」中的一切子节点,运用时用 theme
标签
在任意时刻,程序都是运行在某一特_ 0 7 e m定主题下的,例如 activity 被设置了特定主题
咱们在运用时应该留意 theme 与 style 各自的优势,灵活运用二者
Theme Overlay
这部分内容在视频的 08.55 处
主题是有承继联系的,当该承继联系链中有多个主题装备了同一特点,那么最承继链最底部的内容会收效,鄙人图中,假如多个主题都声明了 colorPrimary,那么 Theme.Owl.Pink 中u v ] F J P的内容会收效(这有点像 Java 的承继联系)
运用这种承继联系咱们能够完成在粉色N / % o主题下将部分界面运用蓝色主题
咱@ W q们看一下两种主题的承继联系,这两种主题的父级应该是比较相似的,这看起来比较糟蹋,因为许多特点是U : w Q h 1 Y :相同的
别的,当你把一个主题设置在另一~ J / U k C个主题之上,你需求留意不能将自己想要保留的东西被覆盖掉N ( W
Theme Overlay
能p ! ! | S t P够很好地处理这一问题,它并不是一项新技术,而是归于一种技巧
接下来咱们. _ 4只需重视想要更改的东西,i : * 3运用下图的声明的主题,则会只改变 colorPrimary 和 colorSecondary 两项特点的值,而其它的一切特点均不变
技巧1:反转色彩
MaterialComponents 供给了 暗色的 Theme Overlay
运用该 Theme Overlay
能够将淡色主题中的某部分做成暗色主题
技巧2:运用正确的 C` | 1 ( s Vontext
咱们知道主题与 Context 相关,因为上文咱们提到的主题的承继联系,运用正确的 Context 很重要
记住:运用距离最近 View 地点的 Context
当然更好的做法是运用主题特点
技巧3:在代码中运用 Theme Overlay
假如想要在代码中运用 Theme Overlay
,能够将其包裹为 ContextThemeWrapper,这也是 android:theme
标签内部做的事情
运用 Theme 和 Style
Color
这部分内容1 O E 9 f ^在视频的 12.m s _ W e36 处
程序内最重 L S 6要a % V a s的资源便是 Coo E & dlor,Android 界说 Color 有许多种办法,比方 Color tag,它首要由J j X I 0 ARGB 色值组成
Color tag 也能够引证其他的 Colo7 , ) I r tag。需求留意的是,你不能在 Color tag 引证主题特点
以上是静态的色彩,下面咱们谈一谈有状况的色彩
Color sta& M (te list 允许你在不同状况下界说不同的色彩,它们也能够用作 drd j e D ^ :awaW 9 = : e c Z Ible,例如下面的比方,在 button 按下或禁用时都有相应的色彩
下面咱们来看一下 Color state list 是怎么界说的,咱们看到这儿运用h M s 9 X H +了 selector
标签,本示例中有两个 item,榜首项界说了一种色– G ; B 4 c – / B彩,并指定它是在选中状况下才被运用;第二项 没有界说任何状况,这意) _ B B G B P D味着7 ! % n z _ p *它是默许色彩。假如没有其他色彩能够匹配当时状况时,它就会被运用
这儿有一个小技巧,能够将 Color state list 依照最简略呈现——最不W / a h X ^简略呈现的顺序进行排序,= f 3 H K这由其背面的完成原理决定,体系会遍历每个 item 直至找出匹配项
在 API 21 官方引入了 android:alpha
tag 来设置透明度,并在 API 23 中能够引证主题特点。假如你运用 AppCompat 的话,它能够向下兼容到 API 14
技巧1:设置透明度
咱们在开发中或许会遇到这种状况:关于同一个色彩,咱们需求不同的透明度。因而咱们或许会仿制不同透明度的色值
你或许不想在更改一个色彩后然后再逐个更改其相应透明度的色值,此处咱们能够运用 ColorStateList,咱们能够运用_ v 6 9 2 _ +默许色彩的功用,只装备一个 item,并在此处装备透明度(从 0 到 1)
技巧2:ColorStateList 与 Drawable 的转化
咱们在 View 装备 background 等特点时能I e O够直接传入 color,在内部体系会填充该色彩并将其包装为 ColorDraw ) r E t 4 ( c uable
但假如你将 ColorStateList 传入是不可的,m a 2在 API 28 及之前的设备会溃散
这是因为 ColorDrawable 是无状况的,在 Android 10 中,官方加入了u A p ; f ColorStateListDrawable 处理了这一问题
为了G $ * ) / L 7 u在一切 API 中取得相同的体会,咱% ,们能够运用一种变通的做法,运用 backgroundTint
此处运用纯色设置了一个矩形,接着运用 backgrounq R YdTint
指向了 ColorStateList
常用技w H : B { 2 E巧
这部分内容在视频的 17.35 处
技巧1:正确命名资源
咱们的项目中肯定有这样命名的资源,它们是依照主题特点命名的。Android Studio 新建项目默许的资源便是这样命名的
而你 0 z i I的主题大概是这样,主题特点指向同名的色彩资源
这样做是不推荐的
咱们需求的不是一个语义z d k I的命] l – K Z Y P _名,而是需求一个文字的命名,咱们能够用品牌色彩命名,也能够像 Material Color System,依据色彩命& 1 ` k F N O名
技巧2:运用一致的 style 称号
大家或许见过这样的款式,一个叫 AppThemk Z be,一个叫 Toolbar,从命名便能够看e b M H f出它们的用途
可是假如咱们加入M s b i 5 H a * w了第三种款式,它的用途不是很明显,咱j A r | ! ,们无法区分它是一个主题仍是款式
为此咱们能够约好一个命名规则
榜首部分为 Style type:主题,款式,文本外观,ThemeB L o w ; Z Ovd u _ $ W q . { ierla/ ^ S f E 4y,形状外观等等
第二部分为 Group name:通常选( L 5 j d 0 w 9用运用称号,u G ^ 3 u ^假如是多 module 也能够为$ * p Y W h module 名
第三部分为 Sub-group name:这姓名通常用于 Widget,也便是运用款式的 View 的称号
第) : ` 4 v Z q #四部分为 Variant name:这是可选的,它是主题: H E的变量
回到开始的比方,依照咱们约好的命名规范| i B 9改造就变成了这样
这儿有一个值得留意的地方,在 Android System 中,.
是一个非常神奇的符号,这儿有一个基于它的隐含的承继体系
上图的 Widget.MyApp.Toolbar.Blue 实践上承继了中心的` { 5 6 7这个主题
这种命名规范能够在 code review 时直观地判别出 style 或 theme 是否用错。如下图,很明显这儿运用了 style 标签,却传入了一个主题
你{ z Y K G乃至能够运用 Lint 来处理此问题,概况移3 , i @ @ G ) P步
技巧3:拆分多个文件
简略形式
将资源类型文件进行规范的分类
-
theme.xm4 G 2 # ~l
:Theme 和 Theme Overlay -
type.xml
:字体,文本外观,l A h M文本尺寸,字体文件等 -
style.xml
:只要 Widget style -
dimens.xml
colors.xmlr V v E
strings.xml
:其它类型归类于实践的资源类F m Y q *型
杂乱形式
杂乱形式是依照逻辑进行分类,例如形状相关的放入 shape.xml
,假如想要完成全屏的UI_ g C,能够在 sys_ui.xml
中操控状况栏/导航栏色彩,以及{ ? r 9 z @ 2是否显示等等
在 Andn F X u # Iroid Studio 的 Android 视图下,这样做的作用是很好的。如下图,能够很明晰的看到 liK ) , )ght 主题和 dark 主题的主题文件
Materiala 5 R ] z x c
色彩体系
这O H o B F l 7 ~ w部m m p {分内容在视频的 24:00 处
该体系构建根底是许多运用语义命名的) F 7 4 ! , # * ?变量,这些变量都归于「主题特点」。它的运作原理是~ . ? W G G } { Y library 展现与这些运用语* X l / D义命名的色彩相关的主题特点,而开发者负责为这些色彩供给1 ^ @ I p数值。在 library[ 2 M * w ! & W 内,用这些色彩y r x = 8构建一切的 Widget
关于色彩体系,开发者需求了解一些常用的色彩
colorPrimary
和 colorSecondary
是 app 品牌的首要色彩,Variant 为主色的比照色;colorSurface
非常有} 4 L L – q用,它负责在某些控件表面的色彩;colorError
是错误的警示色,因而你没Z { % A G W ( [必要在运用时d $ o M 9 6硬编R A r }码这些色彩
色彩体系还会供给一些 On
命名的色彩,这种色彩会保证拥有和相似称号色彩构成U ^ ( l } k E比照的色p t 0 B 6彩^ g K ` ^ d Q [,例如 colorOnPrimary
永久会和 colorPrimary
构成比照
你能够在j 6 * J自己的主题中装备这些色彩,留意S l ] [这儿不必装备一切的色彩,( q D ! B K ,假如你承继了一些 Mater3 * s 0 Q r g 1ial 主题,它们会供c } j T 9 d给一切色彩的默许色,比方下图中没有设置 colorSurface
,则会运用 Material Light 主题内界说的 colm b u s 6 E HorSurface
之后a : p你便能够在 layout 或 style 中运用这些色彩了
技巧
一个有用的技巧是能够将这些色彩与 ColorStateList 结合
比方咱们想做一个分割线,不必创建名为 colorDivider 的新色彩,m j s $ u t 9 &直接从 col~ G W 4 T / u oorOnSurface 中取一个色彩即可,这个色彩肯定会和布景色构成比照
而且它会响应不同的主题,在淡色主题下,20% colorOnSurface
是一种黑P X ` r ? J中带白的色彩。在暗色主题下,colorOnSurfacez i 1 o 1 8
会变成白色,此刻的 20% colorOnSurface
会供a | b l ) D g a给合适的比照
以语义命名的色彩是非常有用的,你能够省去许多的色彩界说
排版体系
这部分内容在视频的 28:06 处
在设计中,通常运用固定的几种8 n O字号进行排版,例如大标题1,大标题2,文本主体,副标题,按钮等等
而这些都是作为主题特点完成的
然后便能够在运用中引证这些主题特点
Material 的 Text 非常强壮,它能够设置行高,假如你遇到了设置行高却不收效的问题,运用 Material 组件能够处理这一问题
形状体系
这部分内容在视频的 30:07 处
Material 选用了 shape system,该体系为小型,中型和大型组件 供给 了主题特点。请留意,假如要在自界说组件上设置 shape,则或许要运用 MaterialShapeDrawable
作为其布景,它能够理解并完成 shape
这儿是经过 ShapeAppearance 来界说的,ShapeAppeI i $ r x N K v QaranS O [ @ S 4 8 Lce 和 TextAppearance 相似,是一种针对形i ; y a 3 %状体系的装备
它由几个组件组成
首先是 cornerFa= @ L }mily,支撑圆角和切角,圆角的方向等等
ShapeAppearance 还支撑 overlay,能够更改特定的 Widget
在运用 overlay 时要留意的是许多 Material 组件是有着自己的 ShapeAppearance overlay,例如 BottomSheet,它会取消底部的圆角
技巧
形状体系是由 MaterialShapeDrawable 完成的
而 MateriL p V alShapeDrawable 有一个强壮的功用便是它有着一个特点叫 int5 4 / C rerpolation
运用它能够D F % q为形状体系做动画,假如它的值为0,那么所d _ h装备形状不会收效,假如值为1,那么形状体系会完整地运用到 drawable 上
暗黑主题
这部分内容在视频的 34:41 处
暗黑主题的适配很简略,能够经过代码设置当时0 k E主题和获取当时F n w /主题
能够运用 MaterialV ] ! v 组件的 DayNight,这样W [ ! 2 4在翻开/关闭 暗黑主题时相应的主题特点的色值都会跟随变化
技巧1 :抽取主3 C f – u r 3 [题
许多时分只做上面的两步并不能很好地适配暗黑主题V w i : x g,例如咱们的运用在淡色主题下是这样的,深色的内容在淡色的布景上
而运用了q { l & 3 9 o夜间主题,或许会变成这样
而咱们想要的作用是这样的
这是因为设置色彩时硬编码导致的
实践上,这种状况运用主题特点会有更好的作用
假如想要 colorPrimary
在B 2 P 1 u ] I不同的主题下运N k a % 用不同的色彩,咱们应该怎么设置?
或许你会在 values-night/colors.xml
为暗色主题界说色值,但不主张这样做!
最好的做法是抽取公共部分到根底主题,然后在此根底上对淡色和深色主题别离装备差异化的特点
技巧2:Co# 4 ? 3 rlorPrimary 的运用
有些时分咱们的 colorPrimary 是一种亮色,例如下图中的蓝色,但在B a D ]暗黑主题下咱们想运用相对较暗的色彩,例如 ?attr/colorSurface
,Material 组件内部为咱们做好了转化,直接运用 ?attr/colorPrimarySurface
即可
Democ [ s R Z 0 e U ?
demo地址在这儿,假如感觉对你有帮助的话,点一颗小星星吧~
20200705 更新
谈论区有不少小伙伴觉; J ) b $ D得重新创建 activity 时有一个黑屏的作用,切换非常僵硬。为了处理这一问题,我找了相关的材料,在 issuetracker 有人提过 activity.create() 办法导致黑屏的问题,但官方仅仅标记了「已分配n r d d 9 , U P @」,并没有供给处理的清晰的时刻
因而咱们只能寻觅其它的办法,考虑到 recreate 会使 al O v k y L + D PctiD V 1 6 ( r Zvity 重建,因而咱c D 4 ( _ * & – d们能够考虑在动画上 做些文章,比方将透明度柔A L {软地过渡
这是之前的作6 : * F ) m 0 4用
尽管过渡作用不是特别完美,但比最开始的僵硬切换要好一些
还有一种办法是不运用 recre6 G Tate ,在重启 activity 时运用 finish + startActivity 并关闭动画 A T来处理,但这样会导致本来的数据丢掉,乃至 ViewModel 都非同一实例,因而我不是很喜欢这个办法
至于谈论区提到的运用 view.getDrawingCache() 的办法,这篇文章有介绍 ,此处不再赘述
关于我
我是 Flywith24,我的博客内容已经分类整理 在这儿,点击右上角的 Watch 能够及时获取我的文章更新哦
-
掘金
-
简书
-
Github