Droidcon SF - Mastering CoordinatorLayout Behaviors

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
again good afternoon everybody whoo there we go that's much better you guys can't be that saturated just yet it's barely in the day one you can't have this attitude until this time tomorrow okay thank you for coming everybody my name is Dave and I'm here today to talk to you about coordinator layout and specifically coordinator layout behaviors just a quick show of hands for those of you in the audience how many have used coordinator layout before those of you how many have tried to write your own behavior for coordinator layout okay that's actually a pretty good number of hands not quite as many so what we're gonna look at trying to do here today is to give you a better idea for how that API works and what the intricacies of are in doing something like building a custom behavior for coordinator layout so that hopefully in a week or so when I ask that question again everybody's hand can go up right we don't want to be afraid of this API we want to get a good idea of how its put together so have you seen me give a talk like this before you'll have a good idea of where I'm going with this but generally what we're gonna try and do is break down this API into smaller chunks that's easier to digest there's a fair amount of surface area here that when you just look at it maybe when you're just going through the documentation it might be a little bit unwieldy as to where you go or what you do in terms of what methods do you override and that sort of thing so we're gonna try and break that down into more useable more manageable pieces today as we go through this process so what is coordinator layout what is it useful for right this is just a quick little animated example that many of you have probably seen something like this done before right this is used heavily in the design support library for managing interactions such as animating the toolbar in and out based on scrolling interactions hiding and showing and shifting buttons based on snack bars and things like that moving into place all of the interactions that you're seeing here are managed with coordinator layout coordinator layout does this using these things called behaviors so coordinator layout is just a container just a view group just like frame layout or anything like that but what's special about it is it has these things called behaviors that allow you to attach relationships to it allows you to take the views and the the child views that are inside of that container and define interactions between them define the relationships for how those views should behave based on what's going on with the other views inside of that layout and that's really what a behavior is responsible for and so we're gonna look at how that looks and the pieces of the API that are available to you to do that so the first thing that I want to do is if I were to take that example that I just showed you and break it down into a diagram of what you were actually seeing it would look something like this so we had a recycler view in there that was scrolling the list up and down the app bar layout for the toolbar section up there at the top the floating action button at the bottom there that had the check mark on it and then every so often you'd see a snack bar pop in from the bottom when that button was clicked okay so all those views are attached to this coordinator layout and what is being done and in fact this is just an example that you would pull out of any of the framework sample code what's being done here is these views are interacting together using a number of behaviors so each of the green dots that you see on this diagram represents a behavior that's attached to one of these views important thing to remember about a behavior is that it can manage interactions or relationships between the attached view and many multiple other views in that container but a behavior can only be attached to a single view so every single one of not all of them but the majority of the views inside of this container have a single behavior attached to them that manages how that attached view relates to the other views around it inside of the coordinator layout okay and you can see in some cases that's a one-to-one mapping in other as a single behavior may actually manage interactions between more than one type of view okay so we're gonna be walking through the different types of behaviors and we'll keep coming back to this diagram as we start to get a little bit more context and feel a little bit more about how this all works together so the first thing I want to give you some experience with is how do you attach a behavior to a view well the easiest way to do it is essentially with magic also known as annotations essentially inside of any custom view class you would like to attach a behavior to if the behavior associated with that view is essentially always the same or fairly consistently the same you can apply that class to the view as a default behavior and simply adding that annotation to that view class ensures that anytime that view is added to a coordinator layout coordinator layout will instantiate and attach the appropriate default behavior on your behalf okay so that's the easiest way to do it in most cases if your building is yourself so you can see the annotation there at the top the other common way of doing this is inside of XML so if you have some attribute or some view living inside of a coordinator layout you can add the layout behavior attribute to that view with the class name of the behavior that you want the framework to instantiate on your behalf and then when this layout is inflated the appropriate behavior will be created and attached to that particular item these are the two most common ways to do this you can actually also attach these in a more direct way by instantiating them yourself in Java code and add them into the views layout and a few other things that it's a very uncommon way of doing things so we're gonna forget that for now these are the two most common ways you would do it either by creating a default behavior annotation or by adding it or attaching it inside of the layout XML so when it comes to actually creating a behavior what do you have to put in it well at a minimum because of these two different ways that these things are instantiated notice that in both of those cases the object itself is instantiated on your behalf the framework creates the object not you you just declare the relationship between that behavior and a particular view so when you create your own behavior class and implement that functionality you have to ensure that you provide the appropriate constructors for the framework to do that job so there's two primary constructors that every behavior effectively should have the first is a default no parameter constructor which is what will be used in the cases of that default behavior annotation or the two parameter version that takes a context in an attribute set that is the constructor that will invoked when this behavior is inflated as part of an XML layout okay so any initialization logic you want to do in a custom behavior has to map through at least one or both of those paths okay depending on the the cases in which you're instantiated a thing notice also that behaviors are typed the type of the behavior that fits into the generic brackets there is the type of view you expect to attach this behavior to this is really just a convenience for you you'll see as we move through some of the other methods that they provide you the type of view that you expect based on this value this has to extend view you can't just attach behaviors to any object but this could be just view if you wanted to create a behavior that could attach to multiple different types of views or view groups okay you can either set that as the common base class of those views or more likely you would just set it to view but if you're always attaching a behavior to a specific view whether it's a custom view you wrote or it's always a floating action button or something like that that's the type of view you would put in here instead okay those are sort of the basics of how you make and attach a behavior but then what about all the rest of the API stuff that's in there and all those other methods to try and simplify this a little bit I've grouped these into two primary categories I believe there are two different types of behaviors that you will typically implement it's usually one or the other you will have what I call layout behaviors and we'll talk about more specifically what that is and scrolling behaviors the layout behaviors are responsible for reacting to view layout view size position those types of changes and there are of the two there the simpler to work with whereas scrolling behaviors are reacting to scroll change events in scrollable containers like a recycler view or something along those lines those are not terribly difficult but are a little bit more involved than doing layout behaviors okay so we'll look at both of those as we move through this so as an example here the layout behaviors are defined by overriding the layout depends on method in the behavior okay so when you're implementing accustomed the first thing that you're gonna need to decide is associated with the view that I'm attached to what other kinds of views does this behavior depend on and whatever that view is whether it's a specific instance or a specific type you would return true from this method when those conditions are met and I'll show you the code for that here in just a second but this method effectively establishes that relationship this declares to the coordinator layout for this attached view I depend on these other types of views whether it's one or many this essentially leads to two specific things the first is it modifies the layout order of the view so coordinator layout actually takes that information and guarantees to you that if you declare in a behavior that you depend on some other view it guarantees that that view will be measured and laid out first so that just in case you need that information to do your own measurement and layout okay and inside of the behavior you also have the opportunity to override the measure and layout of that attached view so the unmeasured child and on layout child methods those are methods on your behavior that you can override if you want to modify how that particular view is laid out based on perhaps the criteria of the dependent view if you don't want to do this you can delegate to the default measure and layout of the attached view itself and even then this dependency still ensures that at least in this example view B is going to be measured and laid out before the attached view a additionally after the view has been laid out if there are any size or position changes in that dependent view if it shifts on the screen or it gets smaller or larger if any of the bounds of that view change then the behavior is going to trigger with an additional set of callbacks on dependent view change and on dependent be removed this allows that behavior again to react to the process or the fact that that dependent view has either moved or is no longer there so for example in the case of the snack bar animation that you saw earlier that floating action button depends on the snack bar so that it can move out of the way when it sees that snack bar come into place or when it goes away okay so that is a layout dependency between the two that allows it to determine based on the position or size of this view what do I need to do as a result okay shift my own content or do something else okay so the on dependent view callbacks allow you to react to those events for any of the other child views inside of the same coordinator layout okay all right so this is some sample code this is basically taken out of the floating action button from the design support library just to give you an example of how this would look in code so we start with that layout depends on method and this is the important part where we declare that in this case for the floating action button what other types of views does the floating action depend on in this case any other child view which is brought in as that dependency parameter any of the other child views if they are a snack bar that's the type of view that it is represented there then this floating action button needs to depend on that views behavior okay in this case it doesn't matter if we have more than one we're gonna declare a dependency on anything else in here that happens to be a snack bar okay you could check the views ID if you wanted to do something more specific the the view is given to you there and this method will be called on the behavior for every other child view currently attached so that you can make this decision for all the other views in in the container having declared that dependency this method on dependent view changed will be called for every change associated with in this case that snack bar now represented as the dependency so in the case of the floating action button when the snack bar position changes or when the snack bar is added or removed to the view hierarchy then the floating action button has to respond to that change by translating its position which is essentially with the contents of that method that I'm not showing is going to do for you so you can see that we declare the dependency and layout depends on and then in the on dependent view changed we have to take we respond to those events as they come associating with whatever change is necessary for the attached view now you look a little further down and it's interesting that there's actually also a dependency in here on the app bar layout but we didn't declare that we depended on any app bar layouts inside of layout depends on so the question is how did that dependency get created if we didn't apply that inside of the logic of our behavior it turns out there's actually another way that these dependencies can be created inside of a coordinator layout and it serves a very specific use case and in this case we're talking about anchors so in addition to just declaring a behavior and attaching that behavior to a view there's a very specific type of behavior called an anchor in the use case where you've probably seen this if whether or not you knew that's what it was doing is in the case that I have here where that floating action button is actually pinned to the bottom of that app bar layout if we were to scroll that view up and down the floating action button would actually move up and down with that view because the two are anchored together so when you set a one view as the anchor of another that is implicitly declaring that there is a dependency on that view that you can react to inside of that views attached behavior now as far as the actual code for moving it that's actually taken care of for you by the anchor by anchoring those two views you're telling the framework that physically position wise they need to maintain themselves relative to each other but it also means that inside of an attached behavior you can react to that change another way so the code that I showed you on the previous slide was not there to shift the floating action button it was actually there for a different reason and again you may have seen this behavior before but as you would typically scroll up in a case like this in a material design application that floating action button does shift up for a while but eventually it disappears and then when you come back down it will reappear so the logic in the behavior is tracking those position changes so that it knows when to hide and show the floating action button not translated so we have this additional specialized use case of a layout behavior an anchor that you can still provide or you can still react to in a customized way if you need to but by default is providing locking of translation and positioned between the two associated views okay so again as I mentioned in this case the floating action button is anchored to the app bar layout and that's why inside of the floating action button there's additional code for an app bar layout as the dependent view okay so if we look at our diagram again we've got a little bit more information now we can see that the floating action button both of the the two views that it has a relationship to are defined in a couple different ways one was defined in code using that dependency layout dependency the other was actually defined using an anchor attribute that I didn't show but is essentially defined an XML okay so by declaring that one is anchored to the other that is also an implicit dependency inside of that layout behavior now it turns out and I didn't show you this but there's also a dependency between the recycler view and the app bar layout which is where we get that that app bar shifting in and out of visibility as the content Scrolls so that is actually defined the same way but I want to look at that in a little bit more detail so you can see here on the diagram that the app bar layout or rather the recycler view has a behavior that is dependent on the app bar layout that means the recycler view is watching the app bar layout for changes okay just keep that in your mind as we go through this so if I were to sort of highlight what's going on here we have those two views and what you may or may not have noticed the first time you look at this is if you were to look at the layout of this view and the bounds of each individual view when they're first put together you may not have noticed that the way these are laid out the recycler view itself is actually laid out to go below the bottom of the display okay it's actually it's tall enough to fill the entire viewport but it's shifted downward to be underneath the app bar layout so it's not actually the size of what you can see it's the size of that plus the additional height necessary for the app bar and then what you may have noticed is when the user starts to scroll upwards what actually ends up happening is the app bar layout moves up and the recyclerview just follows along with it so the first part of that animation you're seeing is there's actually no scrolling going on at all what you're seeing is the two views shifting side by side until the app bar layout becomes completely hidden now the reason that that's working is again because the recycler view is watching the app bar layout so as the app bar moves the recycler view just moves along with it so that exact same layout dependency that I just showed you earlier applies for those two views however something interesting then happens if you were to continue to scroll your finger once it reaches that height point now the contents of the recycler view would actually start scrolling with the same gesture I don't have to lift my fingers start again and then now the content start to scroll with one smooth move of my finger the views are actually sliding upwards and then all of a sudden the content starts scrolling this is something that with the default Android touch api's you just can't do okay there's an additional piece of the puzzle that we have to sort of talk about to understand first of all how that even works and second how we can leverage it inside of our code so I have to take a quick detour here and talk to you a little bit about a feature called nested scroll who's heard of nested scrolling before okay good so well it's been too much time on this just sort of give you the basics so the the overall issue that we have to deal with here is that in Android touch events flow top-down through the view hierarchy they start at the root view and they move their way all the way down until they find a target child view that wants to handle those events but once that target has found that single view has access to all the remaining events in that gesture it's not going to share them with any other views okay so this idea of having a single gesture that's being used to do one thing and then all of a sudden being used by recyclerview to continue scrolling doesn't make any sense until you start looking at what's actually being added inside of the nested scrolling interface so what nested scrolling allows us to do is it allows the parent view of whatever that target is to cooperate with the target view when it cook with regards to scrolling events so what happens is the scrolling child itself is still handling those individual touch events but this nested scrolling interface creates a system where it can propagate back up the chain to the parent view scrolling events that allows that view to participate in what's actually going on here okay so let's talk about that in a little bit more detail in terms of the the visibility of these events to you as a developer so again the same as would be true with any other view any even the scroll view or anything like that touch events are drilling down to the view that you actually touch so whatever that scrolling view is and as the user starts to scroll their finger and that gesture begins those touch events are still being handled by the child view but the first thing that it does when these views implement this nested scrolling interface is they send an event back up to their parent saying I'm about to start a scroll event here's the distance of the gesture that I've computed so far I haven't done anything with it yet would you like to participate in this process and that's called a pre scroll event at this point nothing has actually taken place the contents haven't been scroll and the information that's reported back is the distance of that gesture at least as it's been computed so far because this happens incrementally at that point the parent has an option not only has it been notified that this gesture is taking place but it has the opportunity if it wants to to consume all or part of that event and this is effectively what's happening when in the example that I showed you where the two views are actually moving up and down like that what's going on is that those scroll events are being entirely consumed by the app bar layout and they're using it to shift the app bar layout instead of allowing the recycler view to scroll okay and then it can report back to the child view how much of it was actually consumed did I consume any of it or all of it or some portion it actually returns that value back in terms of the incremental distance and then at that point the child view is free to do whatever it would like with the remainder of that gesture probably scroll its own contents internally in the case of a recycler view once that scroll is complete it will generate another event back up to the parent saying I've finished scrolling okay and there's actually an additional parameter here of any of the additional gesture that even the scroll view may not have consumed now this happens essentially if that scrolling container hits a boundary so if I'm scrolling my finger and the recyclerview hits the bottom of its content but I keep scrolling that additional information now goes back to the parent saying they're still scrolling would you like to do something with this so the parent actually has two different opportunities but during this process that they can participate in the scrolling in addition to what's actually going on in the scrolling child itself okay so let's bring that back to the discussion of coordinator layout and behaviors it turns out that coordinator layout implements that nested scrolling parent interface and views like recycler view implement that nested scrolling child so the coordination or the communication that we typically see is between these two types of views it will work with any two views that implement those two interfaces but coordinator layout specifically as a nested scrolling parent so any nested scrolling child that we put in a coordinator layout like a recycler view will deliver all of its scrolling events up to coordinator layout and then what coordinator layout will do is it will automatically propagate all the events it sees to any attached behaviors okay and that's where we get the scrolling behavior from so the scrolling behaviors are behaviors that you can attach to a view that responds to those pre scroll and scroll events that may be coming off of say a recycler view or some other attached scrolling view inside of the coordinator layout so in this case that that link or that dependency is established by whether or not the behavior returns true from onstart nested scroll this is essentially called every time one of those new gestures begins if none of the behaviors are interested in it they won't see any additional method similar to the way the touch framework works but if you return true from this method then the attached behavior will see two callbacks associated with those two states you'll see one for the pre scroll event and another for the scrolling event and the behavior has the opportunity to do those same things it can consume portions of the scroll and not allow the underlying view such as the recyclerview to take them into account okay so as the user Scrolls you'll see that nested pre stroll and nested scroll event now there's a similar API for a flame just quick show of hands who has no idea what I mean when I say the difference between a scrolling a flame okay most of you understand that just quick overview basically a scroll as a gesture that you stop before your finger lifts whereas a fling is a gesture that there's continues to be momentum when your finger lifts on the screen right it usually generates that animated scrolling of some kind so if the gesture associated with the scrolling turns out to be a fling then that behavior can handle that as well you'll see those types of callbacks come through the nested pre fling and the nested fling as well in the same essential use cases okay so in a in a real application you will probably see both of these where the scroll events will be coming while the user is actually moving their finger but then if they lift it very quickly while it's still moving that will translate into a pre fling followed by a fling okay so you may or may not need to handle both of these depending on what type of the use case you're looking for and I would greatly encourage everyone in here to if you want to have a good example of how all this stuff works together go look at the source code of the design support library that's exactly where all this stuff has used the most and it's all open source okay so the best way to figure out how all this stuff works is go look at the typical example of a bar layout recyclerview and floating action button right wire them all together and look at the code okay I've also got some other examples that I can walk you through and we'll get to those a little bit later on okay so now if we look at this again things are we have a little bit more information even still so again we talked about all the different layout pieces here but now we can see that that final relationship that final behavior where the at bar layout depends on recyclerview that is actually a scrolling behavior so it's a little interesting when you actually see how all this stuff works together you know you touch the scrolling the recyclerview as a user you move up your finger and you assume that that's all being driven by the recyclerview but it turns out that the app bar layout is actually the view listening to those scroll events translating itself based on those events until it no longer needs to translate anymore and then the only reason that the recyclerview moves along for the ride is because recyclerview is watching app bar layout for its position changes so we actually have a little bit of a circular dependency going on in here that goes to how the app bar hide/show functionality actually works inside of the design support library okay okay so that's pretty much all I have for you guys today you know if you want more information obviously I'll have time for questions I'm gonna take questions over here because I'm terrible at hearing and there's no mics out there but you know lots of time to answer questions if you have any issues or anything you like to talk to me about one thing I do want to point you to is my github which you can find easily by just going to mile-high Android comm you'll find a sample project there which is called coordinated effort and there are a number of examples here of using basic coordinator layout behaviors writing custom coordinator layout behaviors from the very simple to an absolute bonkers example of just sliding cards overlapping and moving all over the place but it's all managed with dependencies using behaviors so thank you very much for your time and if I have any have any questions we'll take them down here thank you
Info
Channel: Touchlab
Views: 11,118
Rating: undefined out of 5
Keywords: android, development, design, mobile, web
Id: 22tSgne3ffw
Channel Id: undefined
Length: 28min 39sec (1719 seconds)
Published: Wed Apr 06 2016
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.