RecyclerView Animations and Behind the Scenes (Android Dev Summit 2015)
Articles Blog

RecyclerView Animations and Behind the Scenes (Android Dev Summit 2015)

September 14, 2019


CHET HAASE: Hello and welcome
to the RecyclerView, animations, layout, and more. I am Chet Haase from the
Android UI toolkit team. YIGIT BOYAR: I’m Yigit Boyar,
also from the UI Toolkit Team. CHET HAASE: I’d
like to point out that that’s “Yeet,” not
“Yidgit” or “Yigit,” which we’ve heard a lot. If you spelled .gif with that
kind of g you would actually be if, unconditionally. YIGIT BOYAR: I’ll use this one. CHET HAASE: So I wanted to
point out that first of all, we created a talk
to do in 45 minutes, and we figure the amount
of material we’re covering is about two hours worth. So if people are actually
watching this as a video instead, if you
could just run it at like two or two
and a half x, then maybe we can cram
it all in in time. Otherwise we’ll see how we do– YIGIT BOYAR: They
should run at half x. CHET HAASE: That’s
too complicated. So I also want to
point out that we intend today to talk a little
bit about architecture, RecyclerView, how the
different classes work together to produce animations,
and to create everything that RecyclerView view is. But the thing that we’re
aiming for with RecyclerView overall, especially when
compared to list view, is to give you a lot
of this stuff for free. Right, so it’s flexible, it’s
pluggable, it’s customizable, but ideally you will not have
to understand the details, some of the details that we’re
going to go over today, in order to get the kind of
animated behavior you want to. But if you would like to
customize that behavior, then hopefully the information
we’re talking about today will help. YIGIT BOYAR: OK, so let’s
start with the components, so this is a RecyclerView,
and many of you use the adapter, the layout
manager, and item animator, and some advanced users use
the item decorations or even the pool. But actually all the
RecyclerView architecture is all based on components,
there’s more components that you don’t see
inside, there’s a child helper, which handles
all these views coming in and out. The adapter helper which
handles your adapter updates, and the recycler. So let’s quickly go
through these components, and know what they are doing. Layout manager is simple,
it’s the main component that positions
your items, it can be a simple list
it can be a grid, or it can be a
second grid of items. It depends on the
layout, RecyclerView does not know about it,
or doesn’t care about it. Scrolling is layout manager’s
responsibility again. Focus, so part of the focus
is handled by the RecyclerView as you move it to keep
it, it knows how to move. But if the user reaches
the end of the list, or if layout manager wants
to handle it separately, this is layout
manager’s responsibility to move the other items
up, and bring in new item, and now focus into it. Another example, accessibility,
so layout managers provide basic accessibility,
information to cycle through, this row of this column,
or this row of this column. But if you have more
information about those videos, like the section one cannot
be accessible to focus, you then have access to delegate
to RecyclerView to add this additional information. Let’s go back to adapter. So adapter is the
one that creates your views, binds
the viewholders, tells RecyclerView and
then the data changes. It is also the
place, if you want to handle like click listeners,
there are multiple wheel types, or there’s some recycle recovery
which we will touch later on, and granular data
changes events, like this item is change, not
that the whole list changed. And the important
part about adapter, is understanding the viewholder. Like, this is the
main component. I want to go through how
we create the viewholders. Layout manager at that part,
while we’re on the layout says, OK give me this
view for position x. It checks the cache,
do we have the view, yes we have the view, give it
back to the layout manager. We don’t even
touch the adapters. But first, of course, the cache
says no I don’t have this view, RecyclerView goes
and asks OK what type is this view at
this position, tells the position,
checks the pool, do I have a type of this
view I can reuse. If the pool says no, now it
goes to the adapter and says create me a new
one of this type. It’s not created per position,
like in the listview, it’s created for this type, and
the adapter creates that view. If the pool already has it,
or adapter creates a new one, just binds it back and returns
to the layout managers. And then the layout
manager will eventually add that view to
the UI, this way you get that
onViewAttachedToWindow callback in your adapter. So if you know anything
about life cycle, that’s what you’re looking for. So how do we reserve viewholder? It’s pretty much like we
don’t need this anymore, but we might need
it in the future. So layout manager says, OK
remove and recycle this view, tells adapter OK, this
view has been detached and then checks if
this view is valid. This is important,
valid means, do we still have that type
of it in that position, the contents are up to date. But layout manager doesn’t
want this view anyway, simple example is scrolling. If it is valid, it will let
you move it to the cache. From there we can bring it back
without doing a new operation. And the cache will
invalidate the oldest one, and it’s going to
tell the adapter, OK this view holder
is now recycled. If it is not invalid, and it
will likely go to the pool, and adapter will list it
on onViewRecycled callback. It’s very important
because, this is when like you don’t
receive onViewRecycled, people get confused. Cache is important
because of performance. Another better way
to go to reserves, RecyclerView is
doing your layout, tells the layout manager
layout all the children, and after the layout is
called RecyclerView checks OK, which are the children
that are laid out before, and they don’t exist anymore. For instance,
child recycled then adds them back to the ViewGroup. This is a distinction
between a view being a child of layout manager,
versus a child of RecyclerView. And it hides it
from layout manager, this means these views, which
the layout manager didn’t want any more, there are still
a child of the view group, out layout manager
doesn’t see them, and RecyclerView’s abstraction
handles this distribution. So, calls right through to
RecyclerView, RecyclerView tells item animator,
OK I have this view, can you get rid of this. Item animator runs
the animation, then it comes back in 300
milliseconds later, tells RecyclerView I’m
done with this view. Doing that animation,
this view is not visible to the
layout manager’s API, RecyclerView tells adapter,
OK now I am detaching, cache sends it to the pool. OK very important about
how to use the viewholder. So layout manager calls
removeAndRecycleView, RecyclerView checks if it is
valid, if it is not valid, we tried to send it to
the pool but we check if there’s a transient state. Transient state means
it’s being animated. Like some of the view
properties are being animated. Then the pools ask the adapter,
I cannot recycle this view because this is in
a transient state. Your adapter use should be
up overriding this callback, and handling it properly, which
means get rid of the animation. Well maybe you get to fit your
own bind call, in that case you can just say, it’s
fine like just recycle it, I know how to handle it. If you don’t do anything, and
if the view is in this case is, actually the default
implementation is I don’t know how to handle it. It’s going to be thrown
out, or lose that view. Bad. The moral of the story is
you should use item animator to animate, which Chet
will explain to you soon, right Chet? CHET HAASE: Sure. YIGIT BOYAR: If you have
time, OK, so in other cases recycled pool says is there
another viewholder to pull, the pull says OK I have too
many of these views at type x, and when this is
the case this it says no I don’t want to recycle
it because I already have five of it. And I will kill the view. You don’t want this to happen. So why would this
happen, is basically, you just have too many
viewholders at the same time. We already have
it, why would we? Why would we create
too many of them? Like why did it create, instead
of using the existing one? Well a common case,
this happens where you’re animating too
many of your views through crossfade, which
is like change animations. And I want to show this
here, because I have seen this online, don’t do this. Don’t tell the adapter that all
of your items from 0 to 1,000 has been changed. RecyclerView won’t be able
to reuse any of those views, so how do we fix this? Proper way to fix this is tell
RecyclerView which item has changed, or if you really,
really need those viewtypes, change the default cache
size so that the RecyclerView will pull more of them. Item decorations are your
just regular drawings throughout the cameras. And they can
actually add margins, or offsets to the view bounds. So look at an example, This
is simple item decoration where we draw it a round
ring, and the view. Let’s see the API a
little bit in detail. So this is my view, and
that’s the boundaries of the view by
default. RecyclerView will the item
animator, OK like do you have offsets for
this item, which means do want to around this item? Where you can say
yes, just extend it because I need some more space. And then when the
drawing stars, this is an empty RecyclerView camera,
so RecyclerView will first go OK, draw whatever
you want to draw, before the layout manager
or the children draws. So I draw these
gray backgrounds. Another RecyclerView draw will
happen, the children will draw, and after that we
will call onDrawOver where you can draw over the
children whatever you want. There are some important
things that you should know about item
decorations, first of all, do not try to access your
adapter because you might call it in the middle of a layout. You can still get the
items adapter position but it’s tricky. Instead, go with the
information in ViewHolder. Your view holder should
keep whatever information that item decorator needs. And onDraw rules
apply, don’t allocate because you are being
called many times here in the drawing cycle. And while you need
to get the viewholder you can use this method. OK, the pool is where we
keep all the remaining views. So this is a RecyclerView,
and every single item is under the RecyclerView,
so wouldn’t it be cool if all of them
came from the pool. This is where you
can use a view pool. Alright, let’s go
back to animations. CHET HAASE: Yay. Let’s animate some stuff. So we thought when we were
constructing this talk, we’d talk more generally
about how animations work on Android
first, and then we’d get into the specifics
on RecyclerView. And then we created a talk that
would take two days to deliver, so we’re just going to
assume that everybody knows how animations
work and we’re just going to go on and talk about
the RecyclerView Animations in particular. So first of all,
let’s take a look at the different
kinds of things that can happen in a recycler view. So here’s a really interesting
RecyclerView over there, I want everybody to know
that I did this art myself. OK so we have an ad
operation, so we’re going to tap on
this, or whatever, we’re going to add an item
in between the h and the i, and we’re going to create this
other representation where you see that p element
there, the i and the j got shifted down, and the
k is no longer in the list. We have a delete
operation, so we’re going to delete
the h element there and that’s going to create
this other list, h has gone, I, j, and k got shifted up. And then we have this new
element, l down at the bottom. Or we may have a
change animation, where we’re going to change
the content inside the j item, and it’s going to become
this other content over there on the right. So all very good, and
also all very beautiful. Right? OK, so let’s talk about
animations in particular. So those were the
different states, like the before and after. But then how do you
animate these things, so in the add operation,
say while we’re going to add an item here,
how do we actually transition to this new state of the list? What are the animations
that need to take place? Well clearly there’s
a new item there, so we have some
sort of animation that’s going to fade
that in or otherwise make it appear in the list. And then we have I
and j that shifted, so we could do maybe
some simple translation to move those things
around in the list. And then we have the item
k that no longer exists, so maybe we can do some sort of
fade or disappearing animation to make that go away over time. So for the remove operation,
similarly what animations do we run here? Well first of all
we’re going to need to remove that h
element that went away, we’re going to shift
some things around because they moved
within the list in the before and
the after state. And then we have
this new item there, that’s potentially going to
fade in or otherwise transition to be in the list there. And then finally, we
have the change animation where we’re going to run
some sort of animation to indicate that the
contents of that item have changed over time. So it’s important to point
out that a lot of this stuff just comes baked into
RecyclerView and item animator, and the default layout
managers already. And in fact, even if
you start to customise, there’s still a lot that just
comes for free out of the box. So if an item gets removed
we’re automatically going to fade it
out, if an item gets added we’re automatically
going to fade it in. If it’s moving around in
the before and after state, then we’re going
to translate it. And if it’s changing we’re
going to cross-fade it. Right, we’re going to provide
the two different views that we then cross fade between. And then the question is can we
actually do better than that? And it turns out we can. So first of all,
let’s take a look at what an actual list
looks like under the covers. So that’s what it looks like
on the screen to the user, but really that’s a view
port into the overall list of information that exists
on and off the screen. Right, so you have this virtual
list they’re scrolling through, and you can see the items
here that are represented. They actually exist in some
virtual state off screen. So now let’s take another look
at how these animations might work, given the information that
we have about the off screen items there. So here we said
OK for an addition we’re going to remove
that k element, we’re going to shift
stuff around and add it if it’s in the after
state over there, but in actual fact
in the virtual list that k didn’t go away, it
got shifted off the screen. So that animation, a richer
animation of that k element, is to simply shift it
or move it off screen, to where it would
be if we could see whatever parts of the list
that are currently off screen. Item removal, similarly
we said OK well we need to fade this thing
out, and we’re going to move some stuff over there. And then this l item is
going to appear on the screen because it came from somewhere. Well we potentially
know where it came from, this is the virtual list where
that l didn’t just magically appear, instead it
shifted on screen from where it used to
be in the virtual list. And then, changes. So these are the different
kinds of animations. It’s interesting to sort
of break it down and say what are all the
possible changes that can happen in this
list, before and after, and then how do we
animate all of them. So persistence is when items
are there before and after, whether or not they move. If they don’t move at
all, nothing to do. If they move around in the list,
then you need to move them. Things get added
to the list, things get removed from the list,
and things get changed. But it gets interesting when
you talk about appearance and disappearance,
because here, these are the things where it
got added to the list, but it only got
added to the list because it moved on screen from
where it actually persisted before the layout change. These are the two
types of animations that we call
predictive animations, and there are some parts of
the API that are around this, so if you see the
word predictive, this is what it’s about. It’s about telling
us the information that we need, to know
where things came from or where they’re going to,
when they appear or disappear from the list. So there are two
main things you want to think about when you’re
trying to customize animations. One is the layout manager,
the layout manager is responsible for figuring
out where things go. In the context of animations,
it’s not just where they are, but it’s where they came
from, and where they’re going to be after the layout change. And then on the other
side is the item animator, and that’s responsible
for actually running the animations. So on one side we have
the layout manager to figure out where things were
and where they’re going to be, and then it tabulates
all that information, hands it over to
the item animator, that then takes that
information and says now how do we animate these things. And we have built in
animations in the system, but then you can customize,
given that same information from the layout manager,
in your own system. So on the layout
manager side, you have basically three options. Option one is, you can use
a standard layout manager, we give you a few. We’ve got
LinearLayoutManager, we’ve got GridLayoutManager, and
StaggeredGridLayoutManager. And all of these come
with the capabilities we’re talking about now, not
just a simple item animations but also predictive
item animations. So if you run, we’ll see
a demo a little bit later, but it actually moves
things off screen smoothly or on screen smoothly because it
can tell where these things are coming from or going to. Option two is if you want to
custom layout manager that’s fine. Knock yourself out,
you know how you want to structure your
information on the screen, you’re going to have
a little bit of work to do if you want to
go beyond the basics, but basic item animation
is handled for you. We know how to fade
things in and out, and to cross-fade them, and
to translate on the screen. We can calculate all
that stuff automatically. But it’s not predictive, because
we don’t know your layout manager, so we can’t tell,
for your staggered, grid stacks, huge, things that
you have on the screen, where should that item
have been before we made it appear on the screen
in your strange layout manager. We don’t know so all we
can do is fade it in. And then option three is
to go full bore custom, and say I’ve got a
custom layout manager, and I want predictive
animations so you’re going to need to
enable those, and then you’re going to need to tell
us what we need to know. For the items that go
away, that disappeared off the screen, where
would they have gone if we can still see them. Or if they appear on the
screen, from off screen, where did they come from. So let’s take a look at
some of this information, how do we get this? So it’s the two sides, it’s
the pre-layout where we’re trying to figure out where
they were before they then disappeared, and the post
layout like well where did they go once they
disappeared from the list. For everything that’s in
that list, virtual or non. So, an important element here
is that there is potentially two calls to on layout children. Specifically if you opt
into predictive animations, then you’re going to see two
calls into your on layout children method. The first one is going
to be pre-layout, where we need to know the
information before the layout change happens, including
for the items that are going to go away. And then there’s
the regular layout, where we figure out where
everything is once layout has actually already run. All right, so let’s take a
look at a little bit of code. So we have support
predictive item animations, this is what you need to
override and return true. If you actually want
predictive animations, then we’re going to call you
twice for on layout children. And on onLayoutChildren
this code is taken mostly, stolen mostly,
from linear layout manager which Yigit wrote,
which I would suggest you look at to understand
the basics of how this works. But at a very high level there
are three important elements here, one is first you
detach and scrap the views, so you basically say,
OK take everything off and then I’m going
to tell you how to repopulate it into the view. And then we’re going to
go through all the items that need to be in the list,
and we’re going to add them in. And then we’re going to
account for information if it’s removed then we’re
going to adjust the layout parameters appropriately. And there’s a couple of
important things to note here, the item count
accounts for not just the real items that
are in the list, but if you’re being
called on pre-layout it also accounts
for the items that are in the list prior
to layout running. So we want to know
where all of the items were, before a
layout run, including those items that are going to
go away because of the layout. So you iterate through
those, you put them in place, you add all the views
to RecyclerView, and then finally,
you do this step. And say, well, if I’m not
running pre-layout then I also need to figure out
where the items are going to be in the
virtual RecyclerView and the virtual list
after a layout runs. So once you’ve provided
all that information, then RecyclerView
has the information that it needs to then provide
it to the item animator to run the animations. So in item animator, let’s have
some gratuitous animations. Awesome. So a couple of options
with item animator. One is go ahead and use the
default item animator, that’s why we wrote it. It does a lot of
the basics for you. If you just want
simple animations, recycler view basically
enables default item animator, by default. That’s
why we called it that. Clever, clever
naming, we think a lot about APIs in the Android team. Then go ahead and use it. And you could at least
start from there, and then start
customising as you see fit for some
specialized animations. So it does fade out, and
translate, and it fades in, and it crossfades changes. Option two is, you can get
a little more customized, and you can implement the
animator directly, or implement simple item animator,
or you can subclass. So what we did in the
demo you’re about to see, is we subclassed off of
default item animator, and then just overrode
the specific capabilities that we wanted. So there’s a lot of things
that you can lean on there, so that you don’t have to
actually do everything. So there’s an important
step about recording the pre and post layout
information, chances are if you’re doing
custom animations, you may want more information
than the system records by default. By
default that says what was the view that
was changing here, and what were the
bounds of that view. So then it knows,
basically how big it was, and where it came
from before and after, and it can create some
simple animations based on that information. But if you’re animating custom
content in your view holder, then maybe you want
more information recorded before and after. It’s worth pointing out
that if anybody has taken a look at the transitions,
or the activities transitions APIs, this is very
similar to that, it’s the idea that I need to
record a bunch of information, and then Layout is
going to run, then I’m going to record information
for the same and potentially different views. And then we’re going
to compare them and run appropriate animations. So we record that
stuff before and after, and then you’re
going to override, you’re going to choose
which methods to override. So you can animate the
apparence or disappearance, for things that are
appearing or going away. Whether they’re moving or not. You’re going to
animate persistence, which is the things
that don’t move, as well as the things
that move on the screen but basically are there
before and after layout runs. And then you going
to animate change when the actual contents
in items change. And all of these,
as I said, these have standard implementations
in default item animator, but then you can choose
to override and do something custom if you want. And then an important
point is that when you’re done with your animation
on any particular view holder, you have to call dispatch
animation finished. There’s a little
bit of state that’s being tracked in
some situations, that we need to know
when to get rid of. So a good example, Yigit
alluded to this before, is if you remove a view, and
then we’re going to animate, it we’re going to fade that view
out, we actually take that view and we add it into
the view group so that it can exist so that
we can actually fade it out. You removed it from
the RecyclerView, but we added it in as kind of
a hidden view in the view group just for the purposes of
running the animation. Well we need to know when
the animation is done so that we can remove it then. YIGIT BOYAR: There’s
actually like one common bug that people see, where they
play with the RecyclerView count, which is
public unfortunately, and then they see
shadow views on the UI, because layout manger
doesn’t see them. So layout manger lays out,
everything works fine, but there’s one of those views
that was animating and got stuck there. This is when the proper
APIs are not called back, or we are mucking up with
the internals RecyclerView, so keep that in mind. CHET HAASE: And
finally there’s an item that if you want to
customize the change animation in particular,
there’s new APIs that came out in 23.1, 23.1.1? YIGIT BOYAR: 23.1.0 CHET HAASE: OK, 1.0 support
labor release a few weeks ago, with new change animation APIs. It gives us the ability to not
recycle or create a new view holder– sorry, to recycle and
not create a new view holder, and we can pass it back to you. This avoids expensive,
potentially expensive, things like layout or just creating all
these objects on your behalf. So now instead of simply
cross fading views, for different view holders,
we can actually hand you back the same thing,
and then you can do it totally custom
animation potentially with lower overhead. So let’s take a look at
a demo, fantastic demo, I bet I wrote this. Isn’t this beautiful? So we have a simpler
RecyclerView here, you see me flinging, you
see me flinging back, there’s a linear layout with
a background color on it. There’s a text
view in the middle, the background color
determines the value of the text, its
really complicated. Some of the elements here are,
standard RecyclerView, standard DefaultItemAnimator– you
haven’t seen the animations, yet so that doesn’t
mean anything– standard LinearLayoutManager,
with one caveat– that I wanted to disable predictive
animations, so that we can talk about
some of the artifacts that you’ll see
if you do not have predictive animations enabled–
but LinearLayoutManger actually has them enabled
by default. So yay. All right so there’s a
simple view holder here, it’s just retaining
information about the text view and a linear layout, so that
you can get those later and set the color in the text
appropriately or even animate those values. Simple adapter, so
we have this adapter, we’re going to be
called to bind the view holder with the
appropriate information, we have this database. Very complicated
architecture here, we have a list of colors
here, and we’re to bind the background
color of the container and the text in the
text view appropriately. And then we’re going to be
called to create a view holder, we inflate this
resource, we’re going to set a click
listener– because that’s how I determine where
these things are going to be added and deleted
in this awesome demo that we’re working
through right now– and that’s really about it
in the basic RecyclerView. And then we’re just going
to get some default behavior for the delete
the add animation, so we can see the
delete here, you can see the touch point
and a little white circle, we’re clicking on items there
and we’re deleting those. And we fade the thing out that
got deleted, we shift stuff up that got moved in the recycler
view before and after. And then we fade in a
new item at the bottom. The delete operation
is very simple, we remove it from
the list that we had, the appropriate position there. And then we do a
notifyItemRemoved. It’s important to
tell RecyclerView the thing that happened, don’t
just say something changed, the more information
you give us about the specific range, the specific
item, the specific action, the better able we are
going to be to actually take advantage of that and give
you a richer experience. If you just say
something changed, and we don’t have stable
IDs and can’t determine what those things
are that changed, you’re probably not going
to get the rich animations and interaction that you want. OK so let’s take a
look at add animations, once again, we’re going
to click an item there and then we’re going to add one
into the middle of the list. We’re going to
fade the thing out of the bottom that’s
going away, we’re going to shift some
things on the screen, then we’re going to fade
in the new item there. Decent animations, but I
think we can do better. This is very similar to
the delete thing, where instead of calling
notify delete, we do a notify item inserted. After we’ve added the
color into the list. All right so here are the
artifacts I was talking about, so you can sort of
see these delete and add operations again. And you can see that,
this is supposed to be a contiguous list. You’re basically breaking
the users’ mental model, they think there’s a list,
but then why is this thing. Why is there that
weird janky thing at the bottom where things
are fading in and out. Now it doesn’t look
like a list it, just looks like a
jumble of items there. So that’s the thing that
we want to get around with predictive animations. So if we take a look at
predictive animations for the fade case, you can see
that now when items go away they move off the
screen very logically, into the position that
they’re still occupying within the virtual list. Same thing with delete, when
items come in from off screen, they’re moving in a
very predictable manner that matches what the user
thinks about this list structure. So some code to enable to this. First of all, here’s my
awesome linear layout manager, custom class, where basically
just enables or disables predictive animations
just for the purposes of running the show demo. YIGIT BOYAR: You
should OpenSource it. CHET HAASE: Yeah we’re
going to open source just that one snippet of code. That’s really the
most interesting bit in the entire demo. We’re looking into open
sourcing the rest of it, but that’s the one that I
really want to get out there. Maybe we can write
an article on my blog about that,
medium.com we’ll write an article about that snippet. Check there. So default change animations,
you can see as we click it, you get a crossfade. We’re doing a cross fade
of the background color, as well as the text. Everything all at the
same time, basically we have the old m we
have the new view, and we simply do an alpha
cross-fade between them. It’s decent, but
maybe we can make that a little more interesting. And that’s with the standard
default item animator, so with a custom
item animator, we’ve got a little bit something
more interesting going on. So instead of cross fade
in between these two views, we’re actually treating the
entire view hierarchy in there. The linear layout and
the text view separately, and doing different
things with them. So the background color is
fading to black and then back up to the new
color, and the text is rotating out of place,
and then with the new value rotating into place. So how do we do that? First of all, we say that we
can reuse the view holder, because I don’t want
to create a new one. I simply want to
animate the items that are in the existing view
holder that we already have. And then we extend
default item animator, specifically to customize
the change in animation. So here, we’ve got the
get item holder info, this is a data
structure where we cache the information
that we’re going to retain before and after. So that we know the values
that we actually need to animate before and after. And then this is
going to be called by both the pre-layout record
operation, as well as the post player record. So basically when you
customize this stuff we’re going to call
your code and say OK, record whatever you want
before, and now layout is run, record whatever
you want to after. And then your
animation calls are going to be called with
those data structures, so that you can then take
that information that you recorded earlier and
do something with it. Here we override the
animate change method and you can see, in
the method parameters, we’ve got the holder, the old
holder and the new holder, which in this case is actually
going to be the same view holder information. And we have the pre
info and the post info, so that’s the data
structures that we filled in ourselves when we
were called before and after. And then let’s see
how this works. So we create some
animations here. We have one animation
that does a fade to black from the old
color, and then fade from black into the new color. And we structure that
inside an animator set, and sequence those to
run one after the other. And then the text rotation
is on the text view itself, so we’re going to do a
rotation of zero to 90 to rotate it so that it’s
perpendicular to the viewer. And then we swap the text out–
we’ll see on the next slide– and then we animate the
text back into place with the new value of it. And again we run
those sequentially. Animator set run one, run
the other, and we’re done. I’m cutting out a
little bit of code, there are some
interpolators in there, but I just wanted to
get the high level view of what’s going on there. And then we add a
listener on the animation so that we swap the
text appropriately, so we’re going to
use the old text. So we’ve already run layout,
and our reused view holder has the new text in it. Well that’s not going to
make any sense if we’re trying to rotate the
old text, out so when the animation starts
we substitute that with the old text, and then
we run that half an animation to rotate it out of view so the
views or viewer can’t see it anymore. Then we swap in the
value of the new text, and we rotate it into place. Change animation, now we
put all this stuff together, we create an animator set. And we add them all together,
and then most importantly we call
dispatchAnimationFinished so that the RecyclerView
can get rid of any state that it retained for these
things along the way. And then we’re off and running. So on the previous
one, if you actually clicked on an item
that was animating, you’d get some kind
of janky effects because you’d be in the
middle of an animation. And then a new
animation would start, and they’d be running
at the same time, or maybe you could cancel
one but you would get some discontinuous behavior. So what I really wanted
was, if the user just continuously clicks
on these things, you make that transition
to the new animation as smooth as possible. So you can see we’re
doing that on the right there, were clicking on this
thing and it looks mostly good. If they click during
the second half, well there’s a little
too much information, so we are swapping the
text along the way, but we’re making the
color transition as smooth as possible. So how do we do that? First of all, we need to cache
the running animations we’re going to need to retain
information about what’s actually running on each
item– so we can get that information later– so
we’re going to cache this in this little data structure. And then we’re going
to check it when we’re told to animate a change,
we’re going to say well are there any
animations currently running on this thing, if
there are let’s record some information about that. Are we in the first
half the animation, or the second half
the animation, and how far are we
in those animations. And then we’re going to
seek the new animations, or we’re going to take all
that information from what used to be running, and then
seek in the new animations that we created from
the previous code that we saw before. So that if you’re halfway
through, or quarter of the way through, that first
half an animation, we’re going to seek
a quarter of the way through the new
animation, so it’s going to start at
exactly the same place that the old one left off. And then we’re going to cache
the new animators when we’re done, because we need to
provide that information for the next
interruption that occurs. And that was it YIGIT BOYAR: Thanks, Chet. CHET HAASE: You’re
welcome, Yigit. YIGIT BOYAR: I wish I sold
them before writing them. OK, so we want to touch base
with some important things I about RecyclerView that we
think you should know about. So No Update needs No OnBind. This is one problem
that people have when transitioning
from listView, and come with the
same expectations. RecyclerView is different. So if you don’t tell that
an item has been updated, we are not going to rebind it. How do you update an item? So, for example, if you say
ItemMoved, the item only moved. I have no idea you are
displaying a wrong number there because you didn’t tell
me the item has changed. So we will just
move it, we are not going to tell that
to the adapter. Why do we do this? Because not calling OnBind means
we are not emulating anything. If you don’t emulate
anything, caches are valid, and kittens are happy. It’s important. So another mistake I always
see is that– making this. You cannot make position
final in OnBind calls. This goes back to the same
example in the previous slide. Just because an
item moved doesn’t mean we are going to rebind it. So if you use that position,
in an example like this, like in a ClickListener,
you’re doing it wrong. So we can easily crash this
code by something like this. You bind it, tied
to position five. You move it, then call onClick. Now you remove the wrong
item from the adapter. So how do you solve this? Yes, this is how we solve it. There’s a getAdapterPosition in
the holder, which will always give you the up-to-date
adapter position for a holder. So if you cannot
use that position, it is better to create that
ClickListener where you create the ViewHolder. So it’s much better to
use onCreateViewHolder to end your callback
so that you don’t keep creating a new object
every time the view is rebounds. And this NO_POSITION
check is also important because in a case where
an item is the deleted but users are quick enough
to click on that item before it is
removed from the UI, you also receive the callback
because the real view, so you should always
check for NO_POSITION. Another component involved
in the API we introduced is this item payload. So when you change an item,
you can say why it changed. For example, I can
say this item position changed because maybe the user
liked it or stopped liking it. The cool part about
this API there is another onBind method, which
you see as a list of payloads. So if the payload is empty, you
just run the regular onBind. But if the payload
is not empty, this means we are reusing
the same ViewHolder. So you know that ViewHolder
was presenting the same item before. A change came for this
particular reason. So the only thing– so
say it’s a like update. The only thing you have to
do is change the button. Because you know the rest
of the ViewHolder is valid. And this same
payload information is also passed to
your item animator, where you can use to decide
on the like change I’m going to animate, the heart. So there’s another
confusion in RecyclerView where what does an
adapter position mean? A layout position, like when
we did it’s only one position. We did it. It was wrong. So we’ll see this example
where we have a bunch of items. The AP stands for Adapter
Position, LP, Layout Position. They’re all the same. It matches. Then the adapter says I
moved the item in position 2 to an item at position 5. So now if you look
at it, the items, their adapter position doesn’t
match their layout positions. RecyclerView knows this, so if
you try to call the adapter, try to get the adapter
position on that view, it will tell you it is at 5. So you can safely
access your adapter. It’s just we did not
run a layout yet. So when the layout runs, items
go to their current positions, and these are in sync again. So if you, for
example, in any event, you’re trying to get the view
involved with the other one, then you would like
to use a layout position because it’s what the
user is seeing at the moment. But if you want to access the
adapter, remove or modify, you use the AdapterPosition. So another mistake
we have seen a lot is why we call– if RecyclerView
calls onCreate, that means it needs a new ViewHolder. So it’s a common mistake. Oh I have a aHeaderViewHolder,
if Recycle asks for it, let me just cache and return it. It told you to create, not
to return the existing one. It would use it if it goes
right, so you cannot do this. You need to be creating
a new ViewHolder. Or get it from some other
pool that RecyclerView doesn’t know about. So, for example,
RecyclerView pools are great. We can reuse the views among
different RecyclerViews, even in your own components. So let me create
this one giant pool for all of my applications,
so I don’t create views again. Yay performance! No. You cannot do this either,
because if you do this, first, you are going to
leak the context. These views are inflated
per their context. They get the styles, they
inherit a bunch of stuff. So if you leave the
activity, the viewer will keep reference
to the context. That will total activity. Everything will leak
or it will crash. The second problem
is the inconsistency, because the context may
change between activities. They may be loaded
with different styles, with different teams. So you cannot do this. You need to create a pool
per activity context. OK, this is another problem
we’ve been seeing where people adapt RecyclerView if it doesn’t
receive a change animation, on background traditionally,
or well, you cannot do this, you need to update your
adapter on the main thread. But people are
clever, what they do is they update their context
on a background thread, and then notify the
RecyclerView on the main thread. You cannot fool RecyclerView. If that was the proper
solution, we will do it, right? We’ll just move it
to the main thread. So that’s not the problem. The problem is while
RecyclerView is calculating your layout, you cannot suddenly
sudden say I just removed item 3. Oh wait, I was binding it. Like, what happens? You can’t do this. You have to update that
adapter on the main thread. And tell RecyclerView instantly. That’s the thing. You update it. Write those methods
in your adapter where every time its
contents are updated, it tells the RecyclerView. Yike! Oh my god, we’re on time. CHET HAASE: Wow. We actually finished in time. So I did want to point out, we
will open source the real demo. We just have to figure out how. So check our feeds
here if you want to. Those are the wrong links for
the Dev Summit at the bottom. YIGIT BOYAR: Oops! CHET HAASE: Ignore the
ones on the lower right. They’re wrong, but check
our Twitter or G+ feeds, and we’ll post something
when we open source the demo. We have two minutes and
50 seconds for questions. Yes? AUDIENCE: [INAUDIBLE] CHET HAASE: Oh,
There’s a mic coming. YIGIT BOYAR: Let’s
wait for a mic. You went past him. CHET HAASE: And we’re
out of time, sorry. AUDIENCE: So, just
want to know, compared with listview is
there any performance impact on RecyclerView? CHET HAASE: Compared
to ListView, is there any performance impact? I would say hopefully
it’s all positive. Some of the stuff that you
alluded to like not calling onBind, because we have more
information about this stuff, we also– because we know the
specific items and actions and ranges that
are being changed, we can do things more optimally. Our cache is more intelligent. YIGIT BOYAR: I think one of
the biggest UI performance improvements comes from
not calling onBind. So if you dispatch
proper adapter updates, you’ll probably
gain performance. Of course RecyclerView
runs more animations, handles more cases, so there’s
a cost of them as well. But I mean you want those
animations probably, so you better use RecyclerView. Important part is, dispatch
proper adapter updates. CHET HAASE: That’s it. AUDIENCE: Thank you. CHET HAASE: Thanks. YIGIT BOYAR: Another question? CHET HAASE: Yes? No? Yes? YIGIT BOYAR: He was making– AUDIENCE: I’m going to be first. CHET HAASE: OK. AUDIENCE: In your opinion, use
of your show, the set on click listener for every
item, on every view. With multitouch
screens right now every item will be
clicked, simultaneously if you click multiple items. Any suggestions to prevent
multi-clicks on RecyclerView? CHET HAASE: Actually
multi-click wouldn’t be an issue in general,
it’s pretty robust, it’s just going to execute
all of them independently. I don’t know why multi-touch
would have an impact on that. YIGIT BOYAR: I’m not
sure if I understand. CHET HAASE: Separate
click events. AUDIENCE: Yes in terms
on the list view, on item selected, as
long as one of them will be notified
that it’s selected, other one is not going to be
clicked at this moment in time. CHET HAASE: These were, or
this is totally different. These were just click
listeners, there was nothing about selection
or active items or anything. Maybe you’re asking a question
that I’m not answering, and that’s very
convenient to me. YIGIT BOYAR: He does
that to me all the time. CHET HAASE: There was one
other person had a question and that’s all we got time for. AUDIENCE: High, I’m a
developer and professor. I don’t know exactly,
feedback or a question. The RecyclerView it’s a
little bit very verbose, compared to an iOS
table view, etc. We have to write a lot of
code to make it happen. And I teach coding
too, to the my students and it’s a hard job to them
to make a simple cycler view, and now you have data binding. So you have some things to write
less code to make a cycler view or something like it in the
future to make more work, building a cycler view better? CHET HAASE: If only
the people that wrote the RecyclerView had
anything to do with the people that wrote data binding. YIGIT BOYAR: So we
were trying to solve a problem with RecyclerView
which was mainly animations. Listview reaches limits on
how much we can flex it, it starts to break down. So the main focus
of RecyclerView is to enable you
to do weird stuff. And right now we
encourage more code because we’re not focusing
on making it easier, trying to enable it. After a while, the API, we
just changed the animation API to enable a bunch of stuff. Once the APIs become
more stable we’re going to provide more components
on top of RecyclerView but this is not our focus today. CHET HAASE: First get
the architecture right, then get everything simpler. And not get us into
the listview situation where it works but
it’s terribly fragile. The architecture is
not very flexible, that was old point
with RecyclerView. I think that’s more than the
time that we had, thank you! YIGIT BOYAR: Thank you. [APPLAUSE] [MUSIC PLAYING]

Only registered users can comment.

  1. Ok great topic, great explanation, but ithe speaking should be more articulated, it's hard to understand the words of the performer especially with his gabbling…

  2. This was by far the most complicated talk out of all the Dev Summit presentations. It seems that RecyclerView's APIs are still in flux and evolving. And it is very powerful in regards to animations. But given all the complexities and edge cases that these speakers mentioned, it is very easy for a developer to code RecyclerView logic incorrectly.

  3. could you please release the code for this? I'm just curious to know if I'm doing this correctly as there are alot of moving parts. Thanks for the presentation!

  4. RecyclerView无疑是Android SDK中最复杂一个View. 功能非常强大,不过使用起来一点也不简单。

    1.Adapter的notify…()系列方法中有些会回调onBindViewHolder()有些不会。

    2.holder.getPosition(), getAdapterPosition(),getLayoutPosition(),getOldPosition()之间的区别。

    3.onBindViewHolder()中animate view和ItemAnimator中animate View的区别。以及在onBindViewHolder()中animate view对RecyclerView对View的cache机制的影响。

    4. Item在因为位置变化或者删除animating时,「2.」中各个位置的变化。以及ViewGroup中View变化。

    5.Vertical RecyclerView不wrap width, Horizontal RecyclerView不wrap height.

    6.下拉刷新和加载更多分为外部嵌套Layout和添加Header Footer这两种实现方式。外部嵌套Layout这种方案在header footer与RecyclerView协同滚动上非常tricky.而通过添加Header和Footer这种方式,对Adapter侵入性很大。开源社区目前也没有非常完善的方案。当业务要求adapter的View type非常复杂时,position和view type的对应关系就变得非常复杂。

  5. Okay, finally got the demo code cleaned up and posted to github:
    github.com/google/android-ui-toolkit-demos/tree/master/RecyclerView/RecyclerViewAnimations

  6. I have implemented the Recyclerview animation using this library

    https://github.com/wasabeef/recyclerview-animators

    but now it is creating RemoteException or deadobject exception. Can u help me in solving my problem?

  7. Are you kidding me? This is ridiculously complicated. I'm lost in the first five minutes. And I've implemented a dozen RecyclerViews! Could you find someone who could obfuscate the subject even more? Please, I'm dying to find out who makes these design decisions! Perhaps it's the same person/committee that designed the Android life-cycle. <raspberry>

Leave a Reply

Your email address will not be published. Required fields are marked *