[MUSIC PLAYING] NICK BUTCHER: Hello, everybody. And thanks for coming. So my name is Nick Butcher. CHRIS BANES: I'm Chris Banes. NICK BUTCHER: And we are
both engineers on the Android Developer Relations team. Now the theme of
this conference seems to be modern Android
development, which is why we're super excited to
be here talking about Android themes and styles, which
have remained largely unchanged since API level 1. [LAUGHTER] So despite them having
been around for some time, I still see lots of
developers somewhat confused or frustrated when trying to
use the Android theming system. And I think this
is kind of a shame, because effective use
of the theming system really enables much more
flexible and reusable layouts and styles-- which is great, because it
means you write less code. And that code itself is
much more maintainable. And who doesn't want that? But it does actually enable
some modern practices such as theming. So you can support things
like dark themes or design systems such as material. So I think there's some huge
benefits in understanding the theme system. So while it can be powerful,
it can also be easy to misuse or not understand it. And I think a lot
of it comes down to the ergonomics of how we
write these themes and styles. So we get this XML tag which is
like a set of key value pairs, essentially. But I think a lot of the
problem is that it's largely an untyped system. So you can put pretty much
anything inside a theme or style and it'll compile. Android Studio won't complain. It won't give you a handy
little red squiggly underneath. And who here has
been frustrated when they're trying to
apply a theme or style and it's not worked the
way that you wanted it to? I mean, wouldn't it be nice
if you had a tag like this? So if you wanted to define
style, you use a style tag. And if you wanted to define
a theme, you use a theme tag. Wouldn't that be great? Unfortunately that
doesn't exist. [LAUGHTER] So today what we'd
like to do is give you a crash course in understanding
Android's theme and style system, how it
does actually work. We're going to then give you
some opinionated guidance about how we think you
should use it-- how you can get the most out of it. And then we're going to
walk you through applying this to develop dark themes
and support material theming so you can understand it. So let's get started. So understanding
themes and styles. So as I said, you have
this kind of XML syntax, where it's kind of like
you have this named set of keys and values. And you can think of these--
both themes and styles-- as almost like
configuration stores. They're somewhere you
can stuff some values in order to kind of refer to,
apply to something else later on. But really themes and
styles are very different. And I think understanding
the difference between them unlocks the power to
wielding them effectively. So let's zero in on what
the differences are. Now, first up, a style you can
think of as a map where it's a view attribute to values-- whereas a theme is a
theme attribute to values. Let's look at what these mean. So hopefully you've written
a style something like this. So the keys here in the map
are these view attributes. These are things that you
would set in your layouts onto a particular view. And the values are
the resources that are applicable to that attribute. And you're hopefully familiar
of all the different resource types available to you to use. Themes, on the other hand,
are slightly different. The keys here not
view attributes. They're something
called theme attributes. So, critically,
these aren't things that you would set onto
an individual view. There's no kind of like color
primary attribute for any view type. These are really kind of like
semantically-named variables for this configuration store. They're ways to stuff some
configuration into a map in order to pull it out
later on and refer to it. They look a bit like
view attributes. They're defined similarly
in your attr file like this. And they have the same
kind of resource types as we looked at before. But they are different. When we refer to
them, you can then refer to them using
this question mark attr syntax and then
the semantic name for the value you want to pull
out from this configuration store. And the question mark here,
if you haven't seen it before, basically means look
up the current value for this semantically-named
thing in the current theme. And I think this is really cool. I think actually having
this level of separation allows you to do
some cool stuff. So let's walk through an
example of where this matters. So this is example
from the material library, which defines
this theme attribute called color primary. And then, in your
application, you supply a concrete value for
this semantically-named thing. So you set this value. You set this color. But what's cool
about this, I think, is that if you
have to then, say, support a different look
and feel on another screen-- like say a [? Pro ?] screen
has a different color scheme-- you can then separate
that out and define a second theme which sets
a different value for that. And then, say, your
product manager wants you to support
light and dark themes. And then you can provide
different values again under light and dark schemes. So what's really cool, I think,
about this is it isolates the thing you want to change--
the look and feel that you want to change-- into just the place
you want to change it. Consider if we weren't
using theme attributes here. If you tried to do the same
thing-- these different color schemes for these different
situations-- with styles, you'd end up writing
four different styles. But as we saw before, a style is
specific to a particular view. It refers to layout
attributes which are particular to one type of view. So you'd end up having to write
all these different styles for all the different
permutations of views that you use in
your application. You end up with this like
combinatorial explosion of configuration
you have to support. By extracting out and
having this indirection for certain things,
it allows you to just isolate the things that
are changing into the themes, and then use your
widgets and use a theme and get the combination of
those things at the right time. So hopefully that's clarified
the difference a little bit. So the keys are these
configuration stores of view attributes
for styles and theme attributes for themes. They also differ in that the
way that they are applied. So as we saw, a style is
specific to one particular view or the view type, whereas
a theme is applied to-- is associated with a context
and then applied to a hierarchy. Let's look at what
I mean by that. So say you have a hierarchy
in your application-- something like this. If you were to
apply a style to it, it would just apply to
that one particular view. Contrast that to if you were
to set a theme on the same view using the Android theme tag. That would actually cascade
down to all the children because it applies
to a hierarchy. And you are actually
always running under a theme at any one time. So there's always a theme
somewhere above you. So say the activity might
have a theme set on it or it might inherit from the
application or be on that. And as such, each
of these themes interact, and they kind of
apply on top of each other. Now this can be really
useful if you want to build something like this. So here's an application
which has a largely pink look and feel. The color primary
is set to pink. But then, at the
bottom of the screen, we have this kind
of related section which is using a different look
and feel-- this kind of bluey look and feel. So because we can
have a theme that applies to the
whole hierarchy, we can set that blue theme just on
the root of this bottom section and have it apply to
all the views that are inflated underneath it. So let's have a little look
at this stacking behavior. There's a few nuances which
is important to understand. So in this example,
we might have a pink theme set at
the activity or perhaps the view of a
fragment like this. And then when you get
down to the bottom bit-- the blue hierarchy-- we want you
to set a different blue theme. But if we look at
these themes, it's interesting to
understand how they stack on top of each other. So the pink theme-- you would
define the things you want specific to that screen.
[? And you're ?] [? going to ?] [? sink ?] some colors and
[? things ?] and so on. But that probably inherits
from a parent theme-- so like material
components, which is going to set up a bunch of
stuff like some widget styles and so forth. That in turn is going to
inherit for AppCompat, which is probably going to
inherit from the platform theme. So you always have this kind of
stack of inherited styles sat on top of each other. And the thing to
understand here is that the nearest one to the
bottom-- so the nearest child-- kind of wins. So if two levels of themes in
this stack declare the same attributes-- so material
defines a color primary, and then you set your
own color primary-- the one lower down
is going to win. It's going to
override anything-- any parent or any ancestor. So when we have this
hierarchy example again where we have the pink theme
and the blue theme overlaid at the bottom,
there's a few things you've got to be careful of. Firstly, is that
they both probably have very common ancestors. So it's a bit of a
waste, almost, applying exactly the same things
which are already in effect-- like [INAUDIBLE] material
components I probably already in effect. So applying it again
isn't much use. But more importantly, if you set
a theme on top of another one, you've got to be careful
not to override something you've actually wanted to keep. So say the pink theme
sets up some things-- like maybe something specific
like a shape appearance-- for the whole screen that
you actually want to apply, you've got to be careful
not to override it. And that's where a technique
called theme overlays comes in. I see people getting
confused by theme overlays. But they are actually
quite simple idea. They aren't a different
type of anything. All they are is a technique
of using themes in order to do something specific. And what you do is you
declare that it has no parent. You just say this doesn't
inherit from some other thing. And then you only
declare the exact things that you want to change. So this is designed to be
applied to overlay on top of an existing theme which does
provide all the other things that you need for your screen. And I only want to change-- I want to ninja
in and just change these two particular things. So then if we apply this, say,
a theme overlay version instead, then we'll retain all the things
in the pink theme we wanted. We're not loading up all the
AppCompat attirbutes again. And we're just changing
those things we want to do. So theme overlay, it's
a simple, simple thing. But that can be really handy. And a little tip--
there's a bunch of them premade for you that I
encourage you to use. So for example,
MaterialComponents offers a dark theme overlay. So say you have a
mostly light screen. And you have a subsection which
you want to be darkly-themed. You can just set this dark
theme overlay on that hierarchy and it will flip it for you. And there's a light as well. Also because of this kind
of hierarchy behavior, be careful when you're
using that context-- or theme associated
with a context-- when you treating
things in code. So you've probably got some
code out there somewhere in your code base
where you're retrieving the color using ContextCompat. Watch out for which context
you are applying just there, because if you use one
which is too distant-- so you're using one from
the root of the fragment or something like this-- any themes that get
stacked on top of it-- so if you set the Android theme
somewhere in your layout-- it's not going to get picked
up if you use the context which is in [? blue. ?] So you probably want to use
the context of the nearest view so that any kind of themes
you get stacked on top will be picked up. Or better yet, prefer
theme attributes. So theme attributes are built
to work with the theming system. And so I'd encourage you
instead to try and prefer to use theme attributes rather
than named colors wherever possible, because they
work with the system. And if you in code need to do
the same kind of overlaying behavior, you can look at
it using the context theme wrapper, which allows
you to take a context and wrap it, applying
another style, to give you this very
same overlay behavior. And this is what the Android
theme tag does internally. So it really comes down to-- I think a lot of
people get confused like this, where they don't
understand the difference between themes and styles. And say you just want
to change the background color of a button, right? We've all been there. And they try and do
something like this, where they set a style
which sets color primary. And the problem
is that the style is like basically these
view attributes to values. And so you're basically
trying to say, set the color primary
view attribute. Color primary isn't
a view attribute. It's this other
point of indirection, this theme attribute thing. So this doesn't exist. Instead you want to be doing
is understanding the difference between why these
theme attributes exist, how to use them, and set
a theme overlay like this which itself points to-- it sets the color for you. And I like to think
of it as this-- don't bring a style
to a theme fight. Basically, if you understand
the difference between them-- why they exist and how they
interact between each other-- I think it will resolve a
lot of this frustration a lot of people see around theming. So, yeah. Understand the difference
between theme and styles is kind of like the
fundamentals of this talk. Understand why some attributes
exist and how you can use them. And I've really tried to
tease apart the difference between themes and styles. But the reality is they're
best when they work together. So you can and absolutely
should create styles which refer to themes-- like theme
and attributes and so on-- and use themes which set styles. So it's understanding
what they're good at, when you use each,
and how they work together. CHRIS BANES: Thank you, Nick. Nick's just spoke
about the theory behind themes and styles. Now I'm going to go
into how you actually apply this to your apps. So for me the most important
kind of resource in your apps tends to be colors. They're what make
up the majority of things that are drawn. Now there are a number of ways
to define colors on Android. We have the color tag
which is typically made up of an ARGB color value. Now color tags can actually
reference other color tags so you can create aliases. And the one thing
to know here is that you can't reference a theme
attribute from a color tag. You can only reference
other color tags. And so there's static colors. Now when I talk about
stateful colors. So color state lists are a
stateful wrapper around colors. And it allows you to define
different colors that are used on different states. They use the same
state drawable system as drawables but also as views. So that's how they work. So as example here,
you've got button when state pressed equals true. So when the user clicks in
the view, it changes color. It's actually [INAUDIBLE]. But when you disable
that view, it also goes into this kind
of translucent. And that's powered by
a color state list. So if you actually look at how
color state lists are made, we have the selector tag. And in this example,
we have two items. Now the first item
defines a color. So it points to
brand_color_bright. And that will be used whenever
the state_checked equals true state is enabled. And then we have the
second item which doesn't define any states. And what that means is that
it's the default color. So if there's no
other color items which will match the current
state, this will not be used. Now one tip for this is
that you should always order the items in
the color state list from most specific at the top
to least specific at the bottom. And this is because of the
way color state list works. It iterates through
all of the items until [? they ?] find
one which matches. If we actually go
back to the example and swap the order around-- and so the item with no
states is that the top-- what actually happens is that
this is the only color which is ever used. No states means that
it matches every state. And so therefore
any items below it will never be used,
only this one. There's a bit of a [INAUDIBLE]
for you to know about. So ColorStateList
has had a number of upgrades over the years. In Lollipop, we added
the alpha modulation using the Android alpha tag. And then in API 23,
we gave it the ability to reference theme attributes. So now it's actually
a really useful tool in your belt in
themes and styles. Or if you actually
use an AppCompat, it backports it all
the way back to API 14. All of the AppCompat widgets
kind of use this method internally. So you can either use
it manually or a lot of the-- so, for instance,
text color, or whatever it is-- and they actually
use this internally. So you can safely
reference color state lists using theme attributes
in those styles. So one way we can
use color state lists is to avoid this
kind of situation. So I can almost guarantee
you've got something like this, where you've
got a copy of a color with varying alpha kind
of colors in there. Usually it's for
grays, so you'll have like 20 or maybe 40
shades of gray in there. [LAUGHTER] So what we really want
to do is avoid that, because you don't want
to have one color, and then you change it. But then you've to go through
all these random alpha variants somewhere in your app
and change them too. So what we can [? then ?]
is use color state lists. So color state lists don't
have to have multiple items. You can just have one. And we can use that
functionality of the default color to have that single
item be the always color used. So here we're going to have
pointed to a theme called colorOnPrimary. And so we can actually use
the Android modulation, which I mentioned earlier, which
is a fraction from zero to one. And what that will do is that
will take the alpha value from the color and then
multiply it by whatever fraction you gave it. And that way we get 25%
colorOnPrimary here. You can copy this file to be
as many as you need on all the different alpha values. And another gotcha of
the color state lists is something like this. So here we have a color which
we're actually referencing as a background on a view. It doesn't have
to be background. It could be foreground. It could be image source-- whatever it is. But a referencing a
color as a drawable. Now internally, what happens
is that the attribute system will inflate
that color and wrap it into a ColorDrawable. And that's what
actually gets set. But if you try and
do this instead, where you're actually just set
that to be a color state list, that won't work. And that is because internally,
color drawable isn't stateful. So it doesn't actually know what
to do with a color state list. Now this will actually
work on Android 10 and above, because we have a new
class called color state list drawable. And so this is
one gotcha for you that should always test
on different API levels, because not everything
always works. So one workaround
you can actually use to get the same effect
that works on all API levels is this. We going to use
background tinting. So we'll actually set a
rectangle with a solid color. I like to use magenta because
it's very obvious when this isn't themed. And then if we use
background tint pointing to that color state list,
and that will actually give you the same effect. So that's kind of some
stuff about color. Now let's talk about some
organizational stuff. So I've got three top
tips for you here. The first one is about
naming your resources. So I can almost guarantee
you've also got something like this in your app, which
is a number of color resources which are named on
theme attributes you're going to use them. So we've got color primary,
color primary dark, color accent. And then your theme-- you've probably got something
like this, where we've got-- it points-- a theme
actually pointing to a color resource
of the same name. Now we don't actually want this. And the reason is because
we're getting semantic meaning to what is just a color value. It's just [? an ?] A, an
R, a G, and a B value. That's just a color. But we're giving it a name
and a semantic meaning. What we really want to do
is give it a literal name. So if you've got
like a brand color-- in this case, a blue-- it could
be whatever you need it to be-- name it as so. Or if you're using
something like materials, a color system where you have-- it defines colors
in terms of tones-- here we got material blue 500. It doesn't have
to be a material, but if you've got like a tone
system, that's great too. What we've described
here it isn't really a problem just in
themes and styles, it's a problem in general
software engineering. It's kind of-- for
me, anyway, it's interface versus
the implementation-- it's kind of similar to
what Nick was talking about earlier, in
that themes should be kind of the color system
configuration for your app. And anything else underneath
it is just implementation. So interfaces would be your
theme-- that's what you always go through to get a resource. And then underneath you'll
have your resources-- so your colors, your
troubles, your-- whatever else it
is-- dimensions. So that's the first rule-- use
literal names your resources. The second rule is using
consistent style names, which is slightly different. So again, I'm sure
you've got something like this, where you
have these styles here, one called AppTheme,
one called Toolbar. And now it's pretty obvious
from the naming here. You can probably guess
what they're about. AppTheme-- probably a theme. Toolbar-- probably a
toolbar widget style. But now I'm going to add a third
one called BlueThemedToolbar. Now it's not obvious
what this actually is. Is it a theme? Is it a style? Who knows. Now it's unclear because we
don't actually know the type here because themes and styles--
as Nick mentioned earlier-- we always have to
use the style tag. Who knows what it actually is? So what really you
want to do is create some rules that create names. What we have to do is embed
type into the naming system. And that allows you to
actually see what it's for. So here we're going to use what
[INAUDIBLE] [? framework ?] uses-- and also [? AppCompat ?]
and Android X and all the rest of them-- which is a naming scheme,
which is delimited by a period. So our first group
will be the style type. So themes, styles, could be text
appearances, theme overlays, shape appearance--
whatever it is, it's the type of style
you're going to be using. The next is the group name. So this would typically
be your app name. But it could be if you
have a really modular app, could be just the module name. Whatever is the logical
grouping for your styles. The next one is
your sub-group name. So this is typically
used for widgets. So it would be the
class name of the view that you're actually
creating a style for. And then last one is optional. It's the variance. So if you've got a
variant of a main style-- so say you got a theme
[? called ?] app name dot blue, that's a variant
of the main theme. So this will [? be a ?] way. And these can be repeated. So you can have variants of
variants, blah, blah, blah. So if we go back to
our examples here, they would become something
like this is instead. So if you go from app
frame to theme dot my app and then blah, blah, blah
for the widget styles. The thing here to know is that
the dot notation isn't by-- is actually kind of magic
in the [? Android ?] system, and that we have an implicit
inheritance system based on it. So here
Widget.MyApp.Toolbar.Blue actually implicitly
inherits the middle one because of the dot notation. It's built into the system. One by one place where
this really shines-- this naming system-- is something like this. It's code review. So here we have an example
where I'm accidentally using a theme as a view style-- which obviously, as we went
through earlier, isn't good. So here, Nick has called
it a code review-- because we're
actually using that-- and the name embeds that type
of information into the-- we can actually read
it from the name. So [? we've ?] [? caused ?]
that code review-- much better. And we could even
write a lint rule here to actually catch it
for us automatically. And the third example-- that's our second top tip. And the third one-- our last one-- is
splitting files. So here we got the I/O app
from this year, actually. Here we've got our themes. We've got widgets. And we've got our
text appearances. And [? there's ?] only one
file, called styles.xml. Now, that's kind of bad
because that file is huge. I think it's like
600 lines long. And it's just kind of
impossible to actually navigate. And that's because they've
organized all of their files by the actual resource type. So all styles in style.xml,
dimensions, strings, blah, blah, blah-- they're all in the
same file name. So one way we could
do this instead-- that's --_?} one
we could do this instead is what we've called easy mode. So instead, we're to split based
on the purpose of the resource type. So we're going to just put
our themes in our themes.xml. So these are just themes and
theme overlays, nothing else. Then we've going to have
our type information-- so typography, text appearances,
text sizes, dimensions, font files, maybe-- all that stuff. Then we've going to
have our styles.xml. We're going to keep
that, but it's only going to be for our view
styles, nothing else. And then anything
else would just fall into the actual
resource type file names. If you want to go
that extra mile, you can go what
I call hard mode. We're actually going
to go a bit further. So we're going to break out-- we're going to split on
logical types instead. So if you've got anything
to do with shape-- maybe shape.xml. Or another example
which is good is sys_ui. If you're going to
go edge to edge-- which I hope you will are-- you can put all of your
resources related to that. So any Boolean
flags or any colors for navigation bar or
status bar, anything like that-- it
doesn't have to be related to a natural resource. It could be to the way
you're going to use them. And this is really good if
you're using the Android project view in Android studio,
because when you have overrides by some resource qualifiers,
it will grid them all nicely and allows you to see the
difference between them super easily. So that's our three tips. All right. Over to Nick to talk
about material theming. Cheers. NICK BUTCHER: Cool. So let's see what
we can actually build once you
understand and know how to get the most
of the theme system. One thing which I am a big
fan of is material theming. So material theming
is the idea that you can bring much more
brand and personality to your application. The material design
gives you a set of well-tested, well-designed,
researched widgets, but you should bring your own
personality, your own branding, your own look and feel, to them. And so the way it does this
is it exposes these three parameter systems-- typography, shape, and color. And just by providing values
for the typography, shape, and color, you can
actually achieve huge range of expressiveness
in your application so your apps look and feel
very different to each other and they embody your
own kind of brand. It's quite amazing
how much personality you can get with just these
kind of three dimensions. So let's take a look
at how you use this. So the color system,
to start with, is built up on lots of
semantically-named variables. So semantically-named
variables, I've heard of that-- yes, they're
all theme attributes. So the way this works is
the library exposes theme attributes for these
semantically-named colors which you provide values for. And then in the library,
all of the widgets that it's build with refer to
this semantically-named thing, this point of indirection,
and will pull in the colors that you set. So for the color
system, right, there's a few colors you need
to know and understand. There's color primary
and color secondary-- which is kind of your main
brand color and a color to kind of contrast against that-- as well as variance,
which are often used to get some contrast
against those colors. So it might be used
next to it, for example. As ever, semantically-named
are so useful. Like color surfaces, I find
very, very useful as well. So this is kind of
[? immaterial. ?] Everything sits on
a surface, which is at a different elevation level. And so this allows you to
grab the color of the surface. As well as
semantically-named things like color error-- so rather
than having to kind of like hardcode error
[? colors ?] everywhere you can refer to this question
mark after a color error and pull in error colors
and things like that. One of the things I really like
about the material color system is it also gives these
on colors as well. So this is a color
guaranteed to contrast against the
similarly-named ones. So colorOnPrimary, for
example, will always contrast against primary. So say you need to put like
a icon on top of a floating action button. The [? fab ?] is probably
going to be color primary. And then you can
use colorOnPrimary to say tint an icon. And it will always
contrast against it. A really handy one
here is, again, colorSurface and colorOnSurface. So you do this. So you provide these
newer concrete values to these semantically-named
variables in your theme. So when you set up a theme--
so here I'm setting up a blue theme-- I'm setting some of
these theme attributes. But note that you don't
have to set all of them. If you inherit from some
of the material themes-- so material components light,
or just material components, which is a dark theme-- then they provide
sensible defaults. So here, for example, I'm
not setting a color surface. I'm just going to accept the
one that comes from the library and just go in and change
the ones that I actually want to set. And then you can
and should use these in your layouts and styles. So here I am querying what is
the current value for color primary and setting it as the
background color onto a view. And a top tip for this is that
we can use this single item color selector-- color state
list that Chris talked about-- with these colors
to great effect, so that you don't actually
have to define tons and tons of different colors. So for example, you
want to do a divider. Rather than having
to create a new color called color divider
or something like that. You can just get this
color on surface-- which is guaranteed to kind
of like contrast against the background color-- and use an alpha modulation--
so just 20% of it-- in order to get the right color. And the cool thing
about this is it will respond to different themes. So, like, dark for example. So here I am in a
light theme where this divider in the
middle of the screen here is 20% of the
colorOnSurface, which will be black in a light thing. But when you switch
to dark theme, then automatically
colorOnSurface becomes white. And 20% of that gives
you the right kind of shade of gray to contrast. So yeah-- using these
semantically-named colors can be really
useful so you don't have to define tons and
tons of different colors that you then have to
maintain your application. So that was color. Next up-- the typography system. Material recommends you
use a type scale, which is a fancy designery
way of saying, have a small, discrete
number of text styles and apply them
throughout your apps so you don't end up with
15 different text sizes across the different screens. You have this small list
of choice or palette of textiles which
you should refer to in your application
for consistency's sake. So it gives them
these semantic names like headline one, headline two,
body, caption, button, and so on and so forth. And again, these are
implemented as theme attributes. So material design
components defines these green theme attributes. And it defines them
as text appearances. So in your theme,
you can this here. You can set the theme
attributes and point them to your own text appearance. But you don't have to
customize all of them. You can, again, inherit from
the material theme, which will set them up with using Roboto. But you probably want to provide
your own text appearances, which set the
fonts that you want to use like your brand fonts. And then, again, you can then
refer to these theme attributes in your application. So when you set a text
appearance on a text, you use the question mark attr. And then pull in one of
these named text appearances. I'd go as far as to say that if
you're code reviewing and you see someone setting a text
appearance which isn't a theme attribute, you can almost
consider this a [? code ?] [? smell, ?] because it
means you're not going to be referring to this
small pallette. This is the whole idea
of restricting yourself to a consistent
set of type styles. And for anyone who's seen
them using text appearance and got a little bit worried-- because historically
text appearance hasn't supported all of
the text configuration that text view does. There's some things you can't
set in a text appearance-- material components,
just like AppCompact, there's widget substitution. So when you declare a
text view in your layout, what you actually get is
a material text view-- which was added recently--
and that adds some superpowers to the text appearance. So you can actually set more
things in a material-themed app than you can with a
regular text appearance. So in particular, it
supports line height. So if you ever try to set a line
height in the text appearance and wonder why it's
not working, it's because text view on the
platform doesn't support it. But if you're using material
design components, it does. So that was typography. And the last pillar is shape. So material recommends
using shape meaningfully in your application
to convey meaning to be part of your brand. And it recognizes that
you might actually use kind of different
shapes depending on the types of
widget you're using. So it actually splits it into
small, medium, and large shape types-- because you might want
to set a different shape for large components
like a bottom sheet than you might for a very
small component, like a chip. So again, these are-- well, actually, we defined them
using something called a shape appearance, which is kind
of similar analogy to a text appearance. It's a kind of a configuration
for the shape system. And it's made up
of a few things. First off is the family-- so we support rounded and cut
corners, as well as a corner size-- like the radius
or the rounding-- and which corners to
apply it to-- like the top left, bottom
right, and so on. And then once again, you can
figure this in your theme by setting these
theme attributes. So you point this shape
appearance-- small, medium, and large components-- to one of
these shape appearances, which you've set up in your shape XML. The shape system also supports
this idea of overlays, which means that if you
have a particular screen-- so say the cards on this screen
actually want to look different than the theme overlay one,
the one you set in the theme-- you can use this idea of
an overlay to kind of just go in and change the
particular widget or one part of an applied
shape appearance. But be wary that some
of the material widgets define their own
shape appearance overlays-- something
you should be aware of. So if you take, for example,
a look at the bottom sheet-- which comes from material
design components-- you can see it sets a shape
appearance which inherits from the large component
[? m-style ?] which you set in your theme. But it sets an overlay itself. Basically, if you
look at this overlay, it sets the bottom
corners to zero radius. It says a bottom sheet
comes from the bottom, so it shouldn't have
cut-outs from the bottom. So it is very opinionated in
the way it should look and feel. But you need to
be aware of this, because if you're trying to
style a bottom sheet and trying to set those things by setting
your large component shape appearance, it's
going to overwrite-- it's going to overlay-- with these things here. So just be aware that
some widgets do set these. Text fields do the same as well. A pro tip is that all of
the material shapes system is actually pretty much powered
by this one drawable called the material shape drawable. And it's pretty cool, actually. So you construct one by
giving it a shape appearance-- which just, basically,
this [INAUDIBLE] to do in code loading, what
we looked at for the shape appearance system. But one of my favorite things
about the material shape drawable is it has this property
called interpolation, which is basically how much
of the shape system should I apply to the drawable? So if you set zero
interpolation, none of the shaping
system will be applied. If you set one, all
of it will be applied. And you can actually vary that-- or, knowing me, animate it. So here's an example where
I've got a bottom sheet where I'm applying a shape
appearance to it. And I'm varying that based
on the drag of a bottom sheet here. So you can see that the
corner rounding goes from this kind of
rounded shape up to kind of a squared-off
corner as it expands. So it's kind of cool. So those are the three
pillars of material design. So it's color, shape, and type. And to help you play
with these, or understand how they interact with
the different widget set, we have this cool
project on GitHub called material theme builder. So basically it's kind
of a catalog app which shows you all of
these theme attributes and the values that
are set to them and shows how they will result
in what the components will look like for these
given set of values. The idea being here that
you can download this and you can just play. And it has somewhere where
you can see all the theme attributes that are set. And you can vary them. So here I've just changed a
few of the theme attributes. And you can see how it results
in a completely different look or feel. So it's kind of
like a hobby project where you can take a look at it,
and you can vary these things, work out how to-- what you want to customize
to get a widget set that you want to look at-- and then just take
these theme attributes that you set up and put
them in your application. And you can check this out. It's on the material
design components GitHub repo-- or the short link here. Also, to help you understand
how to use this color, shape, type theming, just this week
we've open-sourced and two of these examples-- the
third one is coming-- onto your GitHub. So we have created-- Material has these
material studies, which are kind of
like showing how you might apply material design. And so there's now some
more open source examples of how to do that. So you can check these out on
the material design examples repo. Over to you. CHRIS BANES: Cool. So the final section in this
talk is about dark theme. So dark theme was added in-- well, it was made
public-- in Android 10 as a new system setting. And [INAUDIBLE] for how
you actually implement it. So the easy way to do
this is using AppCompat. We've had DayNight in AppCompat
for a number of releases now. And you can tell it
what night mode to use. So you use one of these two
APIs, tell it what mode to use, and it will do-- it will change the
resource qualifier for you. Second thing we need to
do is change our theme. So here we're using
Theme.MaterialComponents-- easy way to do it is just to
use the DayNight variants. That's what material design
components gives you. And that will
automatically switch between light and dark
dependent on the resource qual that's [? being ?] used. So you might think
that's job done, right? We've done our two tasks. The system's going
to handle the rest. Well, unfortunately, that's not
actually the case what happens. [LAUGHTER] So here we've got a light app. So we've got dark
content, dark text, on top of a white background. And then when we
go into dark theme, sometimes this happens,
where you'll get dark text on dark background. Obviously not great. Really what we want is this-- light foreground text. Now, usually this
happens because we'll have hardcoded colors in
our styles or our layouts. So here we've got
a hardcoded black. That's obviously not going to
work great in a dark theme. Really what we want do
is to always reference theme attributes. So usually there will be a
color theme attribute somewhere that you can use. And as we mentioned
earlier, themes are really the thing which
are supposed to be correct. They're your source of
truth for all styling. So there's a theme
attribute for a color. Use it, because the
theme is the thing which configures your actual styling. So let's quickly go through
the current dark theme. So here we have an example. We've got a primary
and a color secondary. And they point to
two color resources. So the material guidelines
for dark theme-- well, for in general--
say your primary colors should be like a mid-tone. So here we're
using the 500 tone. It doesn't have that number,
but could be somewhat similar. But material guidelines
also say that in dark theme, your primary color should
be a desaturated, lighter version so it doesn't
have so much contrast. The material color
tool available online actually helps you
with this task. So you can put color
in and play around with what your primary
color should be. So here we have our theme--
we're using DayNight. But we have our color
primary theme attribute. But we have two colors. We have one for our light
theme, one for our dark theme. How are we actually
going to vary that? In fact, which one we
going to actually point to? So what we really need
is something to alias between the two colors. So we could do something like
this, where we have a value-- a color primary,
color resource-- in values, and then also one
in values [? dash ?] night. And it aliases between
the two colors. But if you remember back,
we don't want to do this. We're giving semantic
meaning to color values. So I'd say the easiest way
to do is something like this. So we're going to try
and extract your themes, put them into a base theme,
and then add a layer on top. Then we can vary that theme
in our values and values [? dash ?] night, pointing
to whatever varies. So usually this would be a
lot of the theme attributes that Nick was talking about
in the material theming-- color primary, color
on primary, all those. And they will tend to vary
in the different themes. And then we can actually
set whichever color we need. Here we're going to point
to indigo 500 and our light and 200 in our dark. So the next thing we're
going to talk about is the use of primary colors. So this is the Owl
sample that Nick was talking to
earlier, which uses a really bright blue primary
color for its surfaces. Now when we go into dark
mode, that bright blue is a little bit too bright. It kind of defeats the
object of a dark theme because they're
typically used at night. It's a little bit too bright. So material actually
has guidelines on this. It says that you shouldn't
use really bright colors for large surfaces because
they emit too much brightness for the user. Really, what you
want is something like this-- a much more dark,
muted color tone, which kind of actually hasn't matched the
purpose of the user going to a dark theme. Now, really what
we're doing here is switching between
a color primary for our light theme and color
surface for our dark theme. And luckily, the material design
components for Android library has built-in support for this. It has a new attribute called
colorPrimarySurface, which does the aliasing for you. It also has a number of
styles which are built in-- so ActionBar, all
the ones listed here-- you can use these styles
and they'll automatically do this for you. And we can use a default
view style attribute. So here we're going to set it
so all of our bottom navigation views are going to use the
primary surface version. And then we get
something like this. So regardless of whether
in light or dark, we get the actual style we want. Digging into that dark
theme a little bit, you can see here that there's
actually multiple surfaces. [INAUDIBLE] So here we have two
surfaces-- one a card, one a bottom navigation view--
and they're slightly different colors. But when we actually
dig into the styles, they're actually using the
same color surface attribute. So why is that? So that comes down to
elevation overlays. So in light theme,
shadow actually cut the absence of
light so you see them. In a dark theme, you
can't see a shadow-- well, you can't see a shadow so
well because it's dark. So material has a new way to-- a new solution for that,
which is elevation overlays. So as a dark surface comes
to light, it brightens it. And that's the
effect of elevation. A number of widgets,
materials, and components have support for elevation
overlays like these ones here. And we'll skip through this
because we are out of time. But you can look at this later. We'll post this [INAUDIBLE] on
Github so you can actually read this-- this print a custom view
to use elevation overlays. And the goal here is that
when you have your theme-- the goal is that your-- the theme that varies between
light and dark is very small. So here we've gotten a sample
which is seven attributes. You know you're on a
good path if you've got something like this. And we're going to
skip the overview because we don't have time. [LAUGHTER] And we're going
to skip this, too. [LAUGHTER] Thank you very much. [CHEERING, APPLAUSE] [MUSIC PLAYING]