Developing themes with style (Android Dev Summit '19)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[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]
Info
Channel: Android Developers
Views: 47,690
Rating: 4.8782611 out of 5
Keywords: type: Conference Talk (Full production);, pr_pr: Android, purpose: Educate
Id: Owkf8DhAOSo
Channel Id: undefined
Length: 40min 55sec (2455 seconds)
Published: Thu Oct 24 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.