[MUSIC PLAYING]
Hi, my name is Dan Olshansky.
I'm an intern here on the Android team at Google.
And today, I'll be talking about some ListView animation
insertions.
So in this specific demo, what we're going to be doing is
adding a new object to the zeroth index of the data set
associated with the ListView, and then animating that cell
downwards into it.
As that's going to happen, all the other items in the
ListView are going to make space for the new cell,
animate downwards as the new cell animates back in.
And then, just as a cool little extra part to the
animation, we're going to have an image that's part of the
cell animate into its correct location as the new element is
being added to it.
Some of the important things to know for this animation,
some of the few tricks and problems I encountered, is the
fact that when you change the data set, some of the objects
that you may need may no longer be available after the
layout has occurred.
So what you need to do is you need to take a snapshot of all
the items before you modify the data set so that after you
modify the data set, if you no longer have a reference to
some of these items, you can still animate them out of the
bounds of the list view, because you won't need them
after that anymore.
So jumping straight into the demo, all we have here is a
button, little round view to its left.
The list is completely empty.
Clicking Add Row--
Animate creates a new cell and animates it downwards.
And we can add as many elements as you want.
Those start looping.
And as you can see, it animates downwards.
And all the elements in ListView underneath animate
down as that happens.
You can now of course scroll the list if you want to.
But the interesting thing to note as well is that even
though each of these images is part of the data set
associated with that item in the list with that exact cell,
what we're doing is animating the image from outside the
bounds of the ListView into its correct location in the
ListView, where it then stays and persists as you scroll the
list here afterwards.
So then, looking at the code, we can go into Insertion
ListView, which is an object that extends ListView where
you can insert these new items.
And after a lot of other applied code that you can look
over later, all the main logic happens in Add Row.
So firstly, what we need to do here is first get a reference
for an adapter.
And afterwards, what I'm going to do is keep track of the
balance and the drawables of all the items that are in my
ListView before the data set change took place.
So I'm going to loop through all of my visible cells.
And I'm going to take a screen shot of every single cell.
I'm going to store that.
I'm also going to retrieve the balance of every single cell
and store that in a
corresponding hash map as well.
So now, regardless of whether or not those cells are still
there after data set change has taken place, I'll still
have a reference to them.
Then, when you move down here, I actually add the new object
to my data set and add this table ID
corresponding to that item.
Because in my array adapter, I've specified
true for stable IDs.
So that means I need to make sure that a new stable ID is
added to that element.
Then, I notifyDataSetChanged, which will request a layout on
behalf of the ListView.
But since I want this to animate, one of the common
tricks that have been used for a lot of these animations is
using an onPreDrawListener.
So once again, onMeasure and onLayout Layout have already
been called.
All the final values for all the widgets contained within
the ListView have already been calculated but have not been
drawn to the screen yet.
So this is where any custom animations you want to do you
can do them here since you already have a reference to
all the final values.
So going into onPreDraw, we remove the listener and create
a new array list where we're going to store all our
animations.
So firstly, since I know that I'm adding a new cell to the
zeroth index, that's the view that I'm going to want to use
for a lot of my calculations and animations.
And then, what I'm creating is creating a copy ImageView,
because I can't actually animate the view that I need
from within the cells, from outside the bounds, and put it
where I need to.
But what I'm going to do is create a new copy ImageView
that has the same content as the
ImageView in the cell itself.
Then, I'll just animate it into place and then get rid of
it when I don't need it anymore.
So when I'm animating a new row, one of the other things I
do is just animate the text associated with that cell,
fade it in as it animates down.
And in regards to the image, there's some math that I need
to do to determine where it starts and
where it has to end.
But after all that is done, all you need to really know is
that the ImageView in the cell has to be set to invisible
while the animation is happening, while the copy
ImageView has to be visible.
I use an overshoot interpolator and scale the x
and the y properties of the image just to get this little
cool popping-like effect.
And then, I animate it into place in the new cell view.
And once that's done, I can later at the end do animation
and get rid of my copy ImageView and set the
visibility of my normal view back to normal.
So scrolling down furthermore--
this is once the data set change has
already been modified.
So then, I'll loop through all my visible children of the
ListView at this point.
And there are going to be two types of children that I'll be
working with, those that have a corresponding balance in the
hash map [INAUDIBLE] originally
and those that don't.
If they do have a corresponding balance, then
what that means is that they were visible in the ListView
before the animation, and they're also visible in the
ListView after the animation.
So all I need to do is create an object animator that
translates it from its old position to its new position.
If I don't have that start balance, then what that means
is that in the context of this demo, there's only one cell
that can have this configuration.
It means that it wasn't in the ListView before, but it is in
the ListView after.
And since we're only inserting one new item into the
ListView, this has to be the one in the zeroth index.
And since I also want to animate this, I do a little
bit of math in determining what the height is, what the
divider height is.
And then, I do the same thing and animate its transition y
property to get it from where it originally would have been
to where I want it to be after the layout.
And then of course, since I don't need these others
anymore, I remove them from my hash map.
And then, scrolling down here, if I loop through a key set of
all the items that are left in my hash map, what this means
is that these are items that were in the ListView prior to
the data set change but are no longer visible in the ListView
after the data set change.
So these involve items that have been moved outside the
bounds of the ListView due to the insertion of the new item.
And since these items are no longer contained within the
ListView container, what I need to do is use the bitmap
drawables, which I've captured from before, overlay them on
top of my ListView, and then animate their bounds as if
they were still in the ListView.
Since I'm animating the bounds, it's important to know
that every single time the bounds are changed, I need to
invalidate my view in order for that portion of the
ListView to get updated.
And in order to make that a little more efficient, I call
Invalidate with [INAUDIBLE]
of it such that I don't re-invalidate the whole
ListView and redraw the whole thing every single time only
one of the bitmap drawables has animated.
So I set up those animations for every bitmap drawable.
And I also add it to this array that I have
mCellBitmapDrawables, which I will make note of in a second.
And I add the animation to my animator set.
I animate all this together.
And then at the end, I do all the default things like make
my ListView enabled again, make the ImageView in the
actual cell visible again, make the copy imagery
invisible, and things like that.
So now, here in dispatchDraw, this is where I'm going to use
that array of all the bitmap drawables corresponding to the
items that disappeared.
I'm going to loop through all of them and draw them onto the
canvas every single time dispatchDraw is called.
One of the important things to note here is that I'm checking
the size is zero just to make sure that there are no extra
locations done using the new Java object for loops.
And scrolling down furthermore,
there's just some checks.
I'm checking whether I want to animate in the
new image or not.
So for example, if I were to scroll down here and then add
a row, I'm not at the top, so I'm not going to be animating
in that image.
Whereas if I'm up here, the new
ImageView will be animating.
Down here, there's just some more code of how I'm taking
snapshots of the ListView cells and how I animate them
into place, as well as how I'm getting absolute locations on
the screen when I want to know where that extra ImageView
needs to be when I decide where to animate it to.
So that's pretty much it to how you can insert new cells
and animate their addition to your ListView---
thanks.