Placeholder Image

字幕列表 影片播放

  • (inspiring jingle music)

  • (scattered applause)

  • - Hello, everyone.

  • Okay, cool, the mic's working.

  • So, yes, welcome to this talk

  • about efficient Android layouts.

  • If you've never met me before,

  • I've been doing Android drawup for about seven years,

  • first the Travel App company, the Expedia,

  • then currently at Trello.

  • As per usual, my thing doesn't work.

  • All right, I'm gonna give my presentation over here.

  • You know, you can click to engage, to rate sessions,

  • and ask questions, of which I will probably then answer

  • the questions at the end if you ask them.

  • Anyways, so this talks about efficient Android layouts,

  • and when I was writing it, what I found that

  • I was really interested in wasn't so much so

  • the efficiency in terms of performance,

  • but the efficiency in terms of leverage that you have

  • as a developer, and so I started thinking about it

  • in the way that Archimedes was referring to, like,

  • how fulcrums work, where, like, if you just give him

  • a proper place to stand, he can move the Earth,

  • and so that's sort of the focus of this talk,

  • is like, how to get the most leverage

  • as a developer, because a lot of Android teams

  • are fairly small, and you're asked to do a lot of things.

  • And so in my case, it would be if you give me a

  • a standing desk, I will write you an Android app,

  • and I hope that you're not expecting me to make

  • any more terrible jokes this entire time,

  • 'cause it won't happen.

  • So, this is really just kind of a mish-mash of things,

  • there's no narrative, so I'm just trying to cover up

  • the fact that I have no transition here.

  • Let's talk about ViewGroups.

  • In particular, picking ViewGroups,

  • which ViewGroup are you gonna use for

  • any particular layout, and I think the main thing

  • is the simpler the ViewGroup you can get away with,

  • generally, the better, because the more complex ones

  • require a lot more maintenance, and you can run

  • into a lot more bugs, and so on the higher end of things,

  • I'd say probably RelativeLayout's one of the most complex.

  • ConstraintLayout is yet to be seen, but it looks like

  • it's probably going to be more complex

  • than RelativeLayout when it's finally done.

  • Somewhere in the middle of there is LinearLayout,

  • and then down there at the bottom is FrameLayout,

  • which is one my favorites, because it's so simple.

  • And there's a lot of other views in between,

  • but these are the main building blocks

  • for most applications.

  • So RelativeLayout and ConstraintLayout,

  • they sort of occupy the same space in Android right now,

  • which is that they position views relative to each other,

  • and RelativeLayout's sort of limited in this regard,

  • but it's what we've had since the beginning,

  • whereas ConstraintLayout is new and can do,

  • like, all these amazing things,

  • but there's some key problems with both of them

  • besides the fact that they're fairly complex.

  • One of them is that RelativeLayout is fairly slow,

  • and then the issue with ConstraintLayout

  • is that it's fairly alpha-ish at the moment.

  • They haven't officially released it yet,

  • So there's alpha out on Maven central, but a few times

  • they've completely changed the API around, so it's not

  • necessarily production ready.

  • I mean, you could play around with it now, and you could

  • use it if you want, but you're gonna end up with

  • some of that cutting-edge problems that you end up when

  • you're trying to experimental technology.

  • LinearLayout is great, stacking views vertically and

  • horizontally, you can distribute the weight, so this is a

  • simple view, so rows are stacked vertically, and then also

  • I distributed the weight between those

  • two spinners equally.

  • I'm actually fairly okay with nested linear layouts

  • as an opposition to RelativeLayout, and I actually,

  • last time I told someone this, this was like by far

  • the biggest question that I got, is everyone coming

  • to me later saying, "But I love my RelativeLayouts,

  • that's all I use,

  • I was then that nested LinearLayouts are the worst

  • things in existence and so I can't believe that

  • you just said that."

  • SO to head off that question that I was going

  • to be getting on the sessions thing, is that LinearLayouts

  • are sometimes slow.

  • So if you use layout wait, and you nest them pretty

  • deep, then they an get pretty slow, but that's only

  • a sometimes thing, whereas by opposition, RelativeLayout

  • always has to do two passes, it's always slow.

  • So the hope is that eventually ConstraintLayout

  • will be our savior and save us from the situation

  • of having to decide between the two of them.

  • But in the meantime, I think really what's most

  • important is just to focus on profiling.

  • So whatever layout that you do end up with just turn

  • on profile GPU rendering, and see if things are

  • running fast enough on whatever test device

  • that you're using, hopefully like

  • a really slow one.

  • And if you've never used profile GPU rendering,

  • I highly recommend Googling that and looking into

  • that, 'cause then you get these nice bars that

  • show you whether or not you're hitting

  • 60 frames a second, and what sort of things you're

  • spending too much time on if you don't.

  • But really I talked about all that, so I can talk

  • about FrameLayout, which is my favorite layout in

  • the world, because it's so incredibly simple.

  • All it can do is position things based

  • on the parent bounds.

  • So that is, you can position things inside the center

  • of the FrameLayout, or you can position things on one

  • of the eight cardinal directions of the FrameLayout.

  • Wait, am I getting that right?

  • Yeah, eight.

  • So, but there's a lot you can do with this.

  • It turns out that a lot of layouts, if you just wanna

  • have like say, a simple progress bar in the center

  • of some large screen, like that's a FrameLayout,

  • you don't have to do anything complicated with

  • RelativeLayout or what have you.

  • It's also really great as a simple layout for overlapping

  • views, so if you need two views to be on top of each

  • other, FrameLayout is a great container for that.

  • It's also good for things like clickable item backgrounds,

  • so if you have some image that takes up a very small amount

  • of space, but if you wanna have multiple views that

  • compose a single thing that you click, it's good to have

  • like a FrameLayout as the parent of that, that can

  • actually have the click detection so when you click

  • on it, it actually looks like something is happening.

  • So a good example of this, like in the Trello application,

  • is the notification bar in the upper right corner.

  • So this always present on the screen, it's a single

  • FrameLayout, and there's a icon inside of it that,

  • that white icon is always present, and then if you have

  • unread messages, it'll put that little red thing

  • on top of it.

  • And so, the white icon centered and the red icon is

  • actually pegged to the upper right corner, but then

  • you can use the margin or to push it in so it doesn't

  • just ram up against the sides.

  • And on top of all of that, I can just have these views

  • be very simply positioned, and then pair with clickable

  • item background behind that, so when you actually

  • click on it something happens.

  • Another thing I really like using FrameLayouts for is

  • what I'm calling "toggle containers", so if you have

  • two different states that you toggle between, sometimes

  • you just have a single view that you actually change,

  • sometimes I've found it more handy to have multiple

  • views that you can switch between.

  • And so a FrameLayout's a good way to contain two things

  • in exactly the same spot, and then toggle between them.

  • So a good example of that in the Trello app is

  • the avatar view.

  • So this is whenever you represent a member of a card

  • or something like that, if the user has their avatar

  • Seth and we wanna show that, if they've never taken

  • a picture, then we wanna show their initials.

  • And so it's essentially choosing between an image

  • view or a text view.

  • Littler fancier versions of which that allow you to

  • render a circle, but basically lets you toggle

  • between these two.

  • So the avatar view brings up the next thing I wanna

  • talk about, which is view reuse.

  • We use this avatar view all over the application,

  • so these are just three screens, like the Trello

  • board, a open Trello card, some activity on the side,

  • and there's actually I think, three or four other

  • locations we use an avatar view within the application.

  • And so the question becomes, how do I reuse this

  • in multiple places without having to rewrite

  • the code everywhere, 'cause that would be kind of dumb.

  • So the most obvious way is to use

  • something called an include.

  • So if you've never seen it before, the include tag

  • allows you to point to a direct layout, and then it's

  • as if that layout was just copy and pasted into the

  • code right there.

  • And you can't modify much of what you're including,

  • but you can modify any of the layout params, that's

  • any of the things that starts with layout_, so that's

  • a nice way to be able to include something that may

  • have been may have been match-paired, but you don't

  • quite want it to be in the end.

  • But the problem here is that okay, you get the XML

  • in every single location, but you don't get

  • any of the logic.

  • So now I have to come up with some way to then apply

  • like, find these particular views that were in the

  • include, and then add the logic for actually

  • binding that to the view.

  • So what I actually prefer these days is

  • using custom view.

  • So with a custom view, I call, instead of include,

  • I actually just have the view in reference directly,

  • and then you need to write the actual custom view

  • itself, but it's not very hard, because this isn't

  • a custom view that's doing custom drawing or

  • anything like that, it's just taking the place

  • of what would have been in that include.

  • And so with this custom avatar view, I'm extending

  • Framelayout, so I'm saying the topmost is gonna be

  • a Framelayout, remember I'm toggling between

  • the two states.

  • I've got an image view and a text view, and then

  • inside of a constructor itself, it actually inflates

  • all the views that are underneath it.

  • So I don't need to, as a parent using avatar view,

  • I don't have to worry about what's inside of it.

  • It's handling all of that for me.

  • And then I can have this one nice bind method where

  • I take my member object and figure out whether I

  • should be loading an icon or loading the text.

  • So this makes my life a lot easier.

  • One thing worth noting though, if you're using

  • this sort of custom view setup, this is like

  • a very hand-wavy version of what would

  • be the included XML.

  • But if you include the XML like this, you end up

  • with a view hierarchy that looks like this.

  • You wend up with an avatarview on the top, it's a

  • FrameLayout, and then it inflates another FrameLayout, which

  • then has the text view and image view.

  • So obviously, this middle FrameLayout is pointless,

  • we don't really need it, the lint check in Android

  • is particularly harsh when you do this, something like

  • "has no reason to live" or something like that,

  • "has no reason to exist".

  • So we wanna get rid of that, and the way that we do

  • that is through a layout inflator trick.

  • Which is normally when you're using layout inflator,

  • everywhere you'll see it, there'll be a third parameter

  • there, and it'll be false.

  • And that's because most of the time that's what you want.

  • But in this one particular case, you want it to

  • be true, which happens to be the default.

  • And when it's true, what happens is that the XML

  • that's inflated tries to attach itself to the

  • view group that you passed in as the second parameter.

  • In this case, it's this.

  • And then in the XML, if you use something called

  • the merge tag instead of a FrameLayout, what happens

  • is, it tries to then merge these views into the

  • parent view group without any

  • interstitial frame layout.

  • And then so you end up with the hierarchy that

  • you actually want no unnecessary FrameLayouts involved.

  • A third view-specific piece of advice I had to do is

  • with custom drawing.

  • So this is useful in cases of, particularly complex

  • views, you can can save a lot of time by just

  • drawing yourself instead of trying to figure out

  • how to wedge these views into what you want

  • it to look like into normal views.

  • So a good example of this in the Trello app is the

  • labels, so there's these green and blue and red

  • and yellow and purple labels that are

  • on these cards.

  • So when we first had the Trello app out, there was like

  • six colors, and that was it, that was the most you

  • could apply to any card.

  • And whoever was working on it back in the day

  • did not know about custom drawing, and decided that

  • those would just be six views.

  • So that meant that every single card potentially

  • have six views inflated.

  • But then later on, Trello changed this, that it allowed

  • any number of labels to be drawn, so then you could

  • end up with this nightmare scenario, where every single

  • card could have dozens of labels on them if someone's

  • going really crazy.

  • And then we were talking about recycling those views,

  • it just gets really slow, and if you talk about

  • putting something like this on a tablet, it gets really,

  • really slow because you can see even more cards,

  • and it's rendering even more views.

  • So it was much simpler, then to just take all of those

  • views that were being rendered, and instead, have one

  • custom view that draws really simple shapes.

  • So there's sort of two steps to it, and custom views

  • used to be very intimidating to me, I used to be very

  • scared of custom views, 'cause I thought they looked

  • really hard, but they really are not.

  • And the first step is just telling the custom view

  • how big it should be.

  • That is, how much space does it need to take up?

  • 'Cause I have my label view, which is really nice,

  • but no one knows exactly how much space it's

  • going to take up.

  • So on measure is what you use to tell any parent

  • view group how much space you need.

  • And it turns out, a lot of the times, you can actually

  • skip this whole step entirely.

  • And the reason I say that is because in any view,

  • you can specify, "I want this view to be 48 dp by 48 dp."

  • If it turns out that your custom view is just always

  • going to be the same size, like skip this entirely,

  • just define it in your XML, and you don't have to

  • worry about that.

  • In this particular case because the size varies based

  • on the number of labels, I had to write my own measure.

  • And so a quick way of going going through onMeasure,

  • the message signature that gets called, you have these

  • width measureSpec, and height measureSpec, which was

  • sort of confusing to me at first, but it turns out

  • that these are just packed integers.

  • So it's a single integer that, these two parameters

  • basically take the place of four parameters,

  • which is that a width mode in size and a height

  • mode in size.

  • So the size is just a dimension value, the mode though

  • is telling you how it wants you to handle

  • that particular size that it passed.

  • And there's three different MeasureSpecs for the mode.

  • One is exactly, which means the parent view group

  • wants you to be this exact size.

  • The other is at_most, so it take up as much space

  • as possible, and undefined means you get to define

  • whatever ideal width you would like in the situation.

  • And so your typical onMeasure looks something like

  • this, where you grab, and this is just for the

  • width, and then you would copy the same code

  • for the height.

  • You'd grab the mode and the size, if the measure spec

  • is exactly, you probably just wanna pass back the

  • size that it gave you.

  • You don't wanna screw up the parent view group

  • too much, or else it might get confused.

  • Otherwise, calculate what your desired width is,

  • and if the width spec is at most, then make

  • sure that whatever your desire width is not larger

  • than that size.

  • Otherwise, if it's undefined, you just get

  • to pick whatever desired width you want.

  • Then, once you've done this for both the width

  • and the height, you are by contract required to

  • call set measure set dimension, in order to tell

  • the view what you decided for the width and height.

  • 'Cause there's no return value for onMeasure,

  • you just actually have to call this measure at the end.

  • So that's measuring how big the view is, and the second

  • is onDraw, and this one is pretty simple, it just

  • gives you a canvas, and you draw.

  • And so I'm gonna leave this up to you, because what am

  • I gonna say about canvas?

  • This is not a talk about how to use canvas.

  • Another thing worth considering, is maybe in some cases,

  • you don't actually need a custom view, you could just

  • write your own custom drawable.

  • And the advantage here is there you could take

  • this custom-written code and apply it to any different view.

  • So that's good if you want so me special custom

  • background of sorts.

  • In that case, onmeasure just becomes something like

  • get intrinsic height and get intrinsic width

  • on a drawable, and then ondraw becomes draw.

  • But again, I don't wanna spent do much time on this,

  • you can research more about it later, I highly recommend

  • Cyril Motier gave a talk about Android drawables a

  • few years ago, and I highly recommend that talk if

  • you wanna learn more about that later.

  • And I'll be posting these slides too, if you wanna

  • get the links.

  • Alright, styles, let's move away from views,

  • well, not that far from views.

  • But talk about kind of another layer above views,

  • which is styles.

  • So if you are applying XML to a view,

  • this view has no style.

  • Not because it's uncool, but because there is

  • no style tag on it.

  • And then, if you have a style, all it does is I'm

  • creating some style resources, which has the same

  • attribute inside of it, and then the view itself

  • then applies that style on top of whatever, actually,

  • the style's applied first and then whatever attributes

  • are applied on top of it.

  • But essentially then, in the same way that includes

  • or taking layout XML and just stuffing it into

  • a view group, styles basically take a bunch of

  • attributes and stuff it into a view.

  • And so where is this useful?

  • It's very efficient when you need to style a bunch

  • of what I call, semantically identical views the same way.

  • And so what I mean by semantically-identically is that

  • each view does exactly the same thing in your hierarchy.

  • So a good example of this is a calculator, because in

  • a calculator you want all these buttons, or at least

  • the main number ones to look the same.

  • Another way to put it is that all the style views

  • should change at once.

  • So whenever you wanna change something.

  • So if I wanna change the text size of one of those

  • buttons, my expectation is that all of them

  • change at once.

  • So that saves me a whole bundle of time.

  • I see a lot of people though, myself in the past,

  • especially, misusing styles in very inefficient ways,

  • ways that end up biting you in the long-run.

  • And one way is single-use styles, so that is you have

  • a view that's representing a style, and that style's

  • only used once.

  • I feel like that's just extra work that didn't

  • need to be there.

  • Some people really like separating all this code

  • out, but it's so easy to refactor later and create a style.

  • There's even a refactoring option in Android Studio

  • that lets you do this.

  • So, not really necessary.

  • But more importantly, is where you have two views

  • that are coincidentally using the same attributes.

  • So I've got these two text views, and I say, "Oh look,

  • they're using the same text color and text color hint,

  • great, I'll use a style here."

  • But if you look at the ids, you can tell that these

  • two mean something very different from each other,

  • one's supposed to be title, and one's the body.

  • And so what happens is, suppose later on I

  • decide, "Oh I want the title to be like

  • a different color."

  • Well, if I change the color of the title now, that

  • also incidentally changes the body.

  • And so this style which was supposed to be handy

  • is now just a hindrance, because it's very hard to modify

  • that style without having some

  • unintended consequences later.

  • I liken this to in Java, imagine I have two constants,

  • one is like the number of columns I'm gonna show

  • in some grid.

  • And the other is the number of retries I'll do

  • in some http request if it fails.

  • And so I think, "Aw, these are the same value, I'm

  • gonna optimize this and have a single constant."

  • And problematic for two reasons, one is that three

  • is already a constant, but the other is I've lost

  • all semantic meaning.

  • These meant something very different; if I wanna

  • increase the number of retries for http, suddenly now

  • I've changed how my UI looks as well, incidentally.

  • So that's mistakes people can make with styles.

  • So themes are sort of like styles on steroids.

  • Styles you can apply to individual views.

  • Themes are essentially things that you can

  • apply to multiple views at once.

  • And so that can be a view group, it can be an

  • activity, or it can be the entire application.

  • It allows you to apply default styles as well,

  • so if I come up with, I want all of my buttons to

  • look slightly different across the app, without themes

  • I would have to go take that style and actually add it

  • to all of my XML.

  • Whereas with themes I can say, I would just like to

  • have a default style for all buttons, and it automatically

  • gets inflated for everything.

  • And then the last lazy thing that it helps you

  • with, at least in the context of my talk, it allows you

  • to configure your system-created views.

  • So if you've got popup windows or toolbars or something

  • that the system creates, and that's one fewer thing

  • you have to create.

  • But before you could theme on a view level, there was

  • a lot of problems with oh, I have to create some attributes

  • that affect just this one weird popup, but then it screws

  • up another part of my app.

  • But it's very useful for configuring just things that

  • the system will create.

  • So there's three ways to apply it, you can apply it like

  • I said to the entire application, you can apply it to

  • individual activity, if you do that, it ends up overriding

  • whatever's in the application.

  • And on top of that, you can apply to an individual view.

  • And in the view case, it ends up actually overlaying,

  • so you can just overlay like a few changes to it,

  • an individual views theme.

  • And the view themeing is very, very handy.

  • I don't know if anyone here's worked in the days

  • of Holo probably remembers that there was a

  • holo.light.width action bar, and that was because

  • there was no way to theme just the action bar part

  • of the screen differently.

  • So you have to say in the theme, I want to define

  • most of the screen to be light, but I want this one

  • part of it to be dark.

  • Whereas nowadays, you can say, "I would like just

  • a light theme," and then manually apply a dark theme

  • to the tool bar itself, so it makes things so much easier.

  • So in terms of theming, I highly-recommend people look

  • into AppCompat, which is one of the support libraries

  • that Google puts out.

  • If you're not already using it, amongst other things

  • it makes theming a lot easier.

  • For one thing, it gives you material on all devices,

  • like that's the latest design language from Google,

  • and without this, there's a lot of subtle differences

  • between Holo and Material, in terms of spacing, and

  • also in terms of just the visual metaphors

  • that they're using.

  • And so it's so much easier to start from one single

  • baseline, and then theme from there.

  • Another thing is, it gives you all these baseline

  • themes and styles.

  • So you might wanna change the default look of all

  • your buttons, but you don't wanna have to actually

  • go and define a style, which defines every single

  • attribute that a button has to have.

  • You just wanna take the main one and tweak it a little bit,

  • like add a little padding to all of your buttons.

  • And so AppCompat makes it easy then, to take the

  • AppCompat button style and extend from that,

  • and then modify it.

  • Without that, it becomes sort of a nightmare,

  • especially between Holo and Material.

  • And the third really important thing it enables

  • is that it allows you to do view theming pre-Lollipop,

  • in XML, and that was one of my favorite things,

  • because Lollipop had this view theming which seemed

  • really cool, but I was like, "Oh, but you can't

  • get it backported."

  • They actually did manage to backport that all

  • the way back to I think, some API that you shouldn't

  • even be using anymore.

  • I think 11.

  • Sorry people who are still having to support

  • apps on 11.

  • So a few examples of things you can do with themes,

  • the one that gets touted everywhere is the color

  • theming, so in this case, instead of having to use

  • individual drawables for everything, I can just set

  • up colors, and most of the things in Android will just

  • get colored automatically.

  • So bam, it's like a broad brushstroke you can make.

  • These are some examples of applying default styles.

  • So, just in case you've never seen this before,

  • so for example the top line, defines the button style

  • for the entire application, so that gets applied

  • to every button.

  • The spinner item style is handy because what if I

  • just wanna use the built-in spinner item layout

  • row that Android provides, but I still wanna

  • style it a little bit, I can use that here.

  • Text appearance is nice because then the text

  • appearance can apply to text views, and then you

  • can still apply another style on top of that.

  • Another useful thing you can do with themes is

  • that you can set up attributes which are then

  • referenced in your XML.

  • So in these case selectable item background, which is

  • like one of my favorite attributes, if you refer to it

  • with that ?attribute/ instead of the @drawable that you

  • normally use, then it derives that value from the

  • theme instead of going to it directly.

  • So why is this useful, if you happen to have

  • an app that supports multiple themes, it makes it

  • very easy then to swap between those values, but more

  • importantly, your system might have multiple

  • ideas of what a selectable item backgrounds it's,

  • because pre-Lollipop, here wasn't any Ripple drawables.

  • It was just usually just a flat color that you

  • changed to whenever you click on something, whereas

  • post-Lollipop, you wanna have these ripples because

  • it looks really cool.

  • And so if you use a selectable item background,

  • then the theme can automatically figure out which

  • one it wants to take.

  • Alright, I'm gonna do a quick water break before

  • I move on to the next section.

  • Sorry.

  • Alright, resources, so, resources are you know,

  • all the things that go into your app that aren't

  • just pure Java code.

  • And before I can talk about resources, I wanna talk

  • about device configurations.

  • So if we look at this screenshot there's like a whole

  • bunch of things that one can derive about it in terms

  • of its configuration.

  • So for example, I can say it's in portrait orientation,

  • it's got a height of 731 density in a pint of pixels,

  • it's got a width of 411 of them, it's a Nexus 6p,

  • so it's got a density of xxxhdpi, it happens to

  • be in English right now, the English locale,

  • so it's showing English U.S. and it's version 24 'cause

  • it's running the latest n builds.

  • So these are all things that the Android system knows

  • about the device, and you can query this all manually

  • on your own if you want, but actually with resources

  • you can just have it select things automatically.

  • And some of these device things will change throughout

  • execution, some of them won't.

  • So portrait vs. landscape, unless you're locking

  • your orientation, that can change very rapidly.

  • You change the locale, people probably won't change

  • that often, but they can change it while your app's running.

  • And then some things like the density and what operating

  • system version probably aren't going to be changing

  • while you're running your app.

  • So what sort of things do you wanna vary on this?

  • Well landscape versus portrait, I think is a classic

  • example, because it usually presents a different

  • mode of operation.

  • The built-in calculator app, when it's in portrait,

  • only shows four rows, but when it's got more space

  • to stretch out, it can possibly show some of the

  • more cool functions, just by default.

  • Locale is a very easy one, you wanna have your app

  • translated in a different languages, you just have

  • have it select different text strings based on the

  • locale, so on the left it's in English, on the right

  • it's in Japanese.

  • You can have things break on the width of the screen,

  • so on the phone the card when it's opened is small

  • enough that it just decides to take up the full width,

  • whereas at some point, if the device gets large enough,

  • it just looks kind of ridiculous having it be full

  • width, and so we start having a break point at some

  • moment with width.

  • And another example of that would be like our search

  • results, we have the staggered grid view, and again,

  • on the wide tablet it wouldn't make sense to have

  • a single column, it makes sense to fill it up as much

  • as possible, and so we can vary the number of columns

  • based on that.

  • And then also on the mobile phone you can see the top

  • result is some small board display, because it's a

  • small device, whereas on the larger tablet, we can show

  • the nice big rectangle, which would look nicer if that

  • actually had a background, but whatever.

  • So you could do this all in Java code like I said

  • earlier, but it's a lot easier if you just leverage

  • the resource qualifier system.

  • And what this system does is you define alternative

  • resources for different device configuration and then

  • at run time Android will automatically pick the

  • correct resource based on the device configuration.

  • So it's go through an query everything and figure

  • out which of the resources you defined makes most

  • sense in this situation.

  • And so you define this by the names of the folders,

  • so in your resources directory if you have something

  • that's just the default values, that means it has no

  • resource qualifiers attached to it, it's the default

  • fallback in all cases.

  • Whereas if you do a single dash and then a resource

  • qualifier, so this one has one resource qualifier,

  • it's xxxhdpi, and you can have multiple qualifiers

  • if you want.

  • You can actually apply as many qualifiers as you

  • want to a single value, although usually it isn't

  • handy if you do it to many different values.

  • And one other thing worth noting is that if you do

  • have multiple qualifiers, they have to go in a particular

  • order, so look up the documentation, the documentation

  • has this huge table of all the different qualifiers that

  • you can use, and you have to put them in the order

  • of that table for Android to parse it correctly.

  • So that same documentation page also lists

  • out the algorithm, but pretty much it's just

  • like a process of elimination.

  • It tries to find the most specific resource, given the

  • current configuration.

  • So imagine I start with some value, and I've got something

  • in values with smallest width 600dp, smallest width

  • means that regardless of orientation, what is the smallest

  • width that you can possibly have for the device, which

  • is useful for figuring out kind of the device class,

  • like tablet versus phone.

  • And it also has to be in portrait.

  • So then it would select from this if those are true,

  • but if it turns out one or the other isn't true,

  • then it'll start looking to see other things

  • it can eliminate.

  • So then it'll look maybe for just the single

  • sw 600 dp, oh, it turns out the phone doesn't qualify

  • for that, so then it'll see is the phone in portrait,

  • and if it doesn't qualify for that, then it'll fall

  • back to the base values here.

  • So that's why it's hand to have a default value

  • for everything, the only thing you don't really need

  • a default value for is drawables because the way

  • that Android works it'll automatically scale if you don't

  • have something in the right directory.

  • So if you only have xxxhdpi assets and your device

  • happens to be mpi, it'll just scale everything down,

  • which isn't great performance-wise, 'cause having to

  • do all that extra work, but at least you don't have

  • to worry about that when you're developing quickly.

  • So in terms of using resource qualifiers in the correct

  • way, what I think is important is to think

  • of these resources as code.

  • In particular, to think of each resource that you're

  • inserting somewhere as a parameter to some method

  • function, a method or function, and that the parameter's

  • determined based on the device configuration.

  • So for example, if you're thinking about this in terms

  • of code, the code on the left is insane and dumb

  • because I'd have to write a new square function per

  • number that I want to square, whereas the one on the right

  • has this parameter.

  • So you wanna think of it more in terms of the things

  • on the right.

  • So one simple example, on that I like to use a lot,

  • is actually letting the resource qualifier system

  • determine some Boolean logic for me.

  • So this is a simple one where I basically just wanna

  • know whether it's in portrait or not.

  • Yes, you could query this from resources fairly

  • easily, but this is just an example.

  • So I could say, "By default, is portrait is false,

  • and then in particular, when it is in portrait,

  • then it's true."

  • And then I can get this Boolean value out.

  • But this is really handy if you have multiple

  • different configurations and multiple ways that

  • that Boolean could run, it could do all that calculation

  • for you, you don't have to think about it.

  • A more classic example is using it for different layouts.

  • So I could say I'm gonna call set content view, and I

  • have these three different versions of layout, one that's

  • the default, one that shows up in landscape orientation,

  • and one that shows up in portrait orientation.

  • And I made this slide far before I realized it's very

  • improbably to actually end up ever without it being

  • in landscape or portrait, you'd have to have a

  • square screen for that.

  • But basically, it's select the right one of these,

  • but would be more clever, 'cause this you'll probably

  • end up with some duplicated code.

  • 'Cause chances are, there's not that much that

  • changes between portrait and landscape, so it's then

  • if you can use that code reuse, the include, and then

  • it can switch on just that part of the code that changes.

  • So I've got my LinearLayout, and inside of it somewhere

  • there's an include, and that's the only part that

  • changes based on orientation.

  • So now I can have a single activity main, and I can

  • have a layout that's the default one, and then the

  • layout that just modifies in the portrait.

  • Along the same lines, let's look inside that include,

  • suppose both those includes have text view which are

  • supposed to be pretty much the same thing, but all

  • that they really modify on is what the text size is.

  • But this again, this seems kind of like a waste

  • to have two different layouts here, if all I really

  • wanna modify is the text size.

  • So here what I can do is I can then reference a dimension,

  • and then that dimension can be then determined based

  • on the qualifiers as well.

  • And then to take this even a step further, let's suppose

  • we have style somewhere in the application, that's the

  • same sort of thing, all that modified is the text

  • size, so I can again, have this be a dimension,

  • but now this style can be applied all over the

  • the application instead of applying that dimension

  • trick to just one particular view.

  • So in this example, I have an activity main on top,

  • and then by default to go to to one include, but if it

  • happens to be in portrait, it'll go to a different include.

  • Both of those include a text view, which is supposed

  • to be ostensibly the same between both, so they use

  • the same style.

  • And then that style, based on the current configuration

  • determines what the text size is.

  • So you can really go pretty deep with this and

  • write very little duplicated code between all

  • of your layouts, if all that you're doing is changing

  • things based on device configuration.

  • And as an aside, this is why generally speaking,

  • you shouldn't override config changes on Android.

  • That's a pretty common beginner way to get

  • around the problem of, oh, I rotate my phone and then

  • my activity got destroyed and I didn't want that to

  • happen, 'cause where did my all my data go?

  • And then someone says, "Hey, if you just override

  • config changes, everything works out, and all

  • of your data stays around."

  • there's two problems with that, one is that it doesn't

  • necessarily help you because you probably only override

  • config changes for orientation changes, but there's

  • a lot of other ways that the configuration could

  • change on the fly.

  • But for two, it means that you bypass this system

  • entirely, cause you're basically telling the Android

  • system, "I've got this, don't worry about it."

  • This whole resource qualifier system is a major

  • part of the reason why when you rotate your

  • phone, the activity gets destroyed and recreated again,

  • because it wants you to re-inflate everything, because

  • when you re inflate everything something might have

  • changed based on selecting a different layout.

  • Alright, drawables, so this is, let's see how much

  • time I have, good.

  • Alright, so drawables is the last section I wanna

  • talk about, and I wanna outline sort of a nightmare

  • scenario that you may or may not have gone through.

  • I certainly gone through this, many times.

  • So imagine I'm interacting with design, and they

  • send me a mock-up of a new screen.

  • And in particular this is the login screen, and they

  • wanted to add this login with SSL thing at the bottom.

  • So then I look at this, and I start working on it,

  • but then I tell design, "I need this asset, 'cause I'm

  • not good at design or anything, I need you to give

  • this to me."

  • So design says, "Okay, sure, no problem."

  • So they send you over a zip file, you unzip it,

  • and you get this one file that is who knows how big

  • this is supposed to be.

  • So you tell design. "Okay, this isn't enough, I need

  • more than this, I need one in all the different densities."

  • And design says, "Oh sure," they go do some research

  • on how that works, and then they send you back a

  • file like this, and a zip that contains this.

  • So now you've got all the assets that you need,

  • but then you have to go through an rename everything

  • and put it in the right folders, and then import

  • into your project, it's kind of just a pain.

  • And that's a real pain to do with every asset,

  • and then the kicker on top of all of this is that

  • at the very end, design says, "Actually, I wanna

  • tweak the color, and here's a new set of assets."

  • And so now you have to go through this whole process

  • again, and it's a gigantic pain in the ass.

  • So my recommendation here, I've been working with

  • the design team at Trello, and we figured out a whole

  • bunch of ways to reduce all of this pain and all

  • this friction.

  • And it's just made things so much simpler, and it's

  • basically to think of assets as code, as much as possible.

  • Don't think of them as bitmaps that you get from

  • design as much as you can, think of it as things that

  • you can execute in your application.

  • Because then it's so much faster to tweak and change

  • things on the fly.

  • So the first example of this is drawable XML, which has

  • been in Android since the very beginning.

  • And drawable XML are resources that you can define,

  • you can do things like draw simple shapes, you can do

  • things like set up state selectors, so that's where

  • if you press a button and it looks slightly different,

  • that's a state selector.

  • You can use it as a layer list, and this is really

  • handy because if you have two drawables that you

  • actually wanna layer on top of each other with multiple

  • states in between both of them, you might have thought,

  • "Okay, now I need to design to composite all of these

  • images for me."

  • Actually, you can just set up a layer list, and then

  • change the two layers independently, you get that

  • nice composition going on.

  • So a detailed example for this are the login buttons

  • that I worked on once.

  • And so these login buttons are entirely done

  • through drawable XML.

  • So the first step to making these login buttons work is

  • that I need to create that button outline.

  • And the button outline itself is just its own file.

  • So we're not worrying about the click state right now.

  • And it uses a shape drawable, the first important part

  • is you can tell it what type of shape you want, you can

  • also make ovals and stuff like that.

  • But you're limited to very simple shapes with drawable XML.

  • I wanna say that it's mostly filled with transparent

  • space, in fact, that should be the default, but on some

  • older versions of Android it was not defaulting to

  • transparent for solid, I don't remember exactly why.

  • Then I do want that white outline though, so I give it

  • a stroke, which then determines that outline,

  • and I would like to have a small radius on it so it

  • gets that nice little pretty button look.

  • So that's just creating the outline, the blue actually

  • comes from the background of the entire screen, I just

  • put it there 'cause otherwise it would be really hard

  • to see with my white background on the slides.

  • So then we need to add some behavior to it.

  • When I click it, I wanna actually be able to tell

  • that I clicked it, and so we need to add a selector

  • to it, which you can see in this beautiful little

  • two frame GIF.

  • And so the way that I'm doing this is actually layering

  • a selector on top of that outline I just talked about.

  • So I'm using a layer list, and that layer list allows

  • me to take two drawables and put one on top of the other.

  • And I'm saying the top layer is that outline that I just

  • showed you, that's the previous code that I showed

  • you, is what's going on top.,

  • So that's always going to be drawn.

  • And then the other layer is a selector, and the selector

  • just has two states in it, and one of those states

  • is when it's pressed, then I want you to draw

  • this other shape.

  • So again, I'm using another shape drawable in order

  • to determine what should be drawn inside of it,

  • and in this case it's a little simpler, 'cause I can

  • just say, "I want you to have a solid color, but then

  • again, also have the corner so it doesn't end

  • up bleeding out of the corners."

  • And then when it's not pressed and in the default state,

  • then it'll just be transparent.

  • So that's great an all, but then in version 21 of Android

  • they added these nice ripple drawables which look really

  • pretty and doing that requires a while different

  • set of code.

  • And so for that, you end up using something that's

  • a ripple XML, something we added in version 21.

  • And that inside there is where you say what the color

  • of the ripple should be.

  • And then after that, I add, okay, I still have the same

  • button outline as what's actually being drawn in that

  • ripple, but then the last part of this is that I

  • define a mask.

  • And that mask basically says, "This is the outline

  • of where the ripple should appear."

  • And so then the actual solid color inside of it

  • doesn't really matter, it's just the fact that this

  • drawable will draw to this particular area that matters.

  • And so I was able to get away with all of this and

  • have a different version for different versions of Android,

  • by using the resource qualifier system, so at the

  • bottom there's this outline that I'm always going

  • to be using, but then the default button uses the state

  • selector, which will always work all the way back to

  • version one of Android, I think.

  • And then drawable v.21 for that ripple drawable,

  • I can use there.

  • So drawable XML, a fairly good way to skip a lot of work.

  • And then I just had to ask design for the colors, I didn't

  • have to ask them for anything else in particular.

  • Vector drawables, so like I said, the shape drawables

  • lets you draw very simple shapes, nothing complicated.

  • Vector drawables lets you do any sort of vector

  • drawing you want, or most vector drawing that you want.

  • And so it allows you to do very complex shapes, and

  • the advantage here to using a vector is that you

  • don't have to then worry about density of the

  • screen all that much.

  • 'Cause before I was having to get these PNGs from

  • design that was in all the different densities,

  • here they can just give you a single vector,

  • and then it's automatically drawn at whatever's

  • the best resolution for that screen.

  • So that's a huge time-saver, but there is one

  • problem with the way that Android

  • implemented vector drawable.

  • Oh, so another point is that vector drawables

  • were added recently in Android, but there was

  • a back compatible library in the support libraries

  • for using vectors all the way back to I think,

  • 14 or something like that.

  • But there is a big problem with the way that Android

  • did it, which is that they came up with their own

  • vector drawable format that is not actually SVG.

  • And if your designers are anything like my designers,

  • they know how to speak SVG really well, all of their

  • tool know how to output in SVG, and none of them know

  • how to output as vector drawables.

  • So you need some way to convert these SVGs that your

  • designers are giving you into vector drawables in the app.

  • So there's sort of two ways of doing that, yeah, sad

  • design, there's two ways of doing this.

  • And one is in Android Studio you can say, "I want a new

  • vector asset," and that'll bring up this nice little

  • wizard, and then you can pass in the SVG and it'll

  • convert that into a vector drawable as best it can.

  • There are some SVGs that it doesn't work very well

  • with and won't convert.

  • So that's good, but I still am even lazier than this,

  • because I don't wanna have to go through a wizard

  • every time I import a new asset.

  • So instead, we wrote his before all the vector drawable

  • back compat stuff happened, but we're still using

  • it, which is this Android plugin that we

  • wrote called Victor.

  • And what Victor does, is you define any number of source

  • sets, anywhere that you have your SVGs, and it'll just

  • slurp all those up, and then output something that

  • the system can render.

  • And for awhile it just output PNGs but then eventually

  • we were able to get to actually grab the code that

  • is in this new vector asset stuff and then use

  • that to convert it straight into vector drawables.

  • So that's great because actually with Trello,

  • our designers have their own Git repository, which is

  • where they put all of their compiled SVGs, and then

  • we can just have that as a Git submodule, and import

  • it, and then we just have to update a commit pointer

  • to get new assets from design, that's great.

  • And then so the last thing I'd like to say about

  • drawables that has really saved a lot of time

  • recently, is that difference between skeuomorphic

  • and flat design.

  • So skeumorphic design is where you have things

  • that look exactly like what they're supposed to be,

  • and so on the left is Andie Graph, which is an app

  • that my friend wrote, which makes your phone act

  • exactly like any TI-83, Ti-84, whatever,

  • and look exactly like it.

  • So it looks very realistic, that's very skeumorphic,

  • whereas on the right you have the normal calculator,

  • which is flat, and every button is just this flat color.

  • And what's really nice here is that as nice as the

  • thing on the left looks, on the right all the icons and

  • all the text is just a flat color.

  • And what that means is that it's very easy to tint

  • those colors and change them on the fly.

  • With the buttons on the left, it would be very

  • hard to tint them in any way that would be reasonable.

  • So actually, in the Trello app, all of our assets

  • are flat black colors.

  • So they're black on alpha, and then in code, we take

  • any of those, and tint it whatever color we want.

  • And so that's super-handy from the perspective of

  • design, because they don't have to create multiple assets

  • for whatever color they want.

  • Every time they wanna change the colors, we can just say,

  • "Oh, that's easy, let's go change it in code,"

  • and then we're done.

  • So in terms of tinting images, there's sort of a few

  • ways to do it, one is to do it via XML, but besides

  • the fact that image view has had this tint attribute

  • forever, which doesn't quite work all that well,

  • it's not backwards compatible.

  • It got added into recent versions of Android to be

  • able to tint drawables in XML, but they haven't figured

  • out any way to actually backport that functionality.

  • So I ended up doing actually most of the tinting

  • in code, it's very simple with image view and drawables,

  • you can call set colorfilter, and then you just pass

  • in the color.

  • And then it turns out that for a black icon on alpha,

  • you wanna use the Porter/Duff mode of source in.

  • That's actually why the XML imageview of the tint

  • attribute there, I don't like it very much, 'cause it

  • uses a Porter/Duff mode that isn't compatible.

  • I think you have to create white icons on alpha,

  • which we're not doing at the moment.

  • Now if you want a really comprehensive solution,

  • this exists in the support libraries is drawable

  • compat, and with that you actually wrap the drawable,

  • and then you can call set tint, or set tint list

  • on that wrapped drawable.

  • And main advantage that has over just calling color

  • filter directly is that it can handle tint lists,

  • so you can have multiple different selected states

  • for that wrapped drawable in color, and you can

  • tint all of it equally.

  • But since we're not actually using that in the

  • app, we don't end up doing that very often, and so

  • set color filter's just a faster, easier way of doing it.

  • Anyways, so that was all that I've got.

  • This was a slide I was told to put up as well,

  • to let us know what you think, click the happy face

  • if you liked it or the sad face if didn't like it.

  • And thanks very much for coming to listen to the talk,

  • the middle link is to my blog, where I've written

  • more about some of these things and then some

  • parts of this talk were taken from other talks

  • that were more detailed, in particularly the styles

  • and themes, I went into much more detail on that.

  • So you can look at my old speaker deck to find those

  • talks if you're more interested in learning some

  • of the more nitty-gritty details there.

  • And thank you very much.

  • (audience applause)

  • Were there any questions?

  • - Yes, actually there was one of them, and the

  • question was, whether performance was lost in virtual

  • machines and the.

  • My take on it is that, well, the question might

  • mean that if you use like an emulator, and you're doing

  • this, you'll have a slide with the graphics performance

  • in different layouts, will it affect emulators, do you

  • know that, if?

  • - So yeah, so let's see here.

  • - I guess that's what--

  • - I think you're talking about this one--

  • - [Interviewer] (Mumbles) Yeah.

  • Could you actually find out that your layout

  • are kind of--

  • - Through an emulator?

  • No, I'd use a real device for this.

  • - [Interviewer] Yeah, you'd use a real device.

  • - Yeah so I wouldn't, for things like profiling,

  • the emulator can really make things awkward because

  • it could be the case that you have this GPU, if you're

  • working on a Windows desktop or something, you're gonna

  • have a GPU that's bigger than your phone,

  • running the rendering.

  • And that's gonna throw things off a little bit, obviously.

  • Or it could be the case that whatever, I think it used

  • to be that it was translating from ARM into x86,

  • and then that would be really slow, and so all the CPU

  • operations would be really slow.

  • Basically yeah, I would use an actual device for all

  • sort of profiling purposes.

  • - Okay, and do you, you touched upon

  • this new ConstraintLayout, is that something you

  • played with or?

  • - Yeah, I played with ConstraintLayout.

  • - You believe it's the savior, or?

  • - I hope so, 'cause it does a lot of things

  • that, it's sort of like this composite of RelativeLayout

  • and LinearLayout right now, that allows you to create

  • very complex layouts without having too much nesting.

  • And so the hope is that you'll be able to create

  • these complex layouts without much nesting, and that

  • through the constraint system, it can better figure

  • out what are the more performing way to lay

  • things out quickly.

  • So I'm hoping that it saves us from having to

  • use RelativeLayout more often. (laughs)

  • - [Interviewer] Yeah, I think you have more questions.

  • Please come down and talk with them afterwards.

  • - Yeah.

  • - [Interviewer] But thank you very much, Dan, and remember

  • to rate the session and I can see some of the speakers

  • here from the Android track, so thank you very much,

  • it has been a pleasure.

  • And yeah, please give him a hand.

  • (audience applause)

  • - Thank you.

(inspiring jingle music)

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

GOTO 2016--高效的Android佈局--丹尼爾-盧。 (GOTO 2016 • Efficient Android Layouts • Daniel Lew)

  • 57 5
    colin 發佈於 2021 年 01 月 14 日
影片單字