This week I had the time to write a small and cool transition animation between Activities.
The regular transition animations between activities animate the Activity as a whole. I wanted to create an animation that split Activity A into 2 parts, animate them in the way out and revealing Activity B. Here’s a gif that illustrate this:
The idea is fairly simple:
- Save Activity A as a bitmap
- Split bitmap into 2 parts
- Pass the 2 bitmaps to Activity B
- Show the bitmaps on top of Activity B’s layout
- Animate the bitmaps outwards
- User is now seeing Activity B
The implementation was not as straightforward as I thought. I ran into some difficulties on the way, but I found solutions to all the problems I encountered. Let’s go step by step.
Note: This implementation requires holding a snapshot bitmap of the entire screen. For some devices that might be a very expensive operation due to low memory and/or large screen area. If you choose to use this animation - do it with care and don’t overuse it.
Save as Bitmap
In order to get a bitmap of the Activity, we can use this code:
[sourcecode language=”java”] View root = currActivity.getWindow().getDecorView().findViewById(android.R.id.content); root.setDrawingCacheEnabled(true); Bitmap bmp = root.getDrawingCache(); [/sourcecode]
On the first line, we get the root view for the activity. We find the view with the id of _android.R.id.content, _which is the FrameLayout that exist on every layout and contains the layout you put on setContentView(). Here’s how it looks on HierarchyViewer:
In order to get a bitmap of the root view, or any view for that matter, we call the getDrawingCache() method. This will return the cached bitmap of this view in case we have enabled the caching for it. That’s why we also call the setDrawingCacheEnabled() before that. If there isn’t a cached bitmap for the view – it’s been created on the spot.
Split the Bitmap
In order to split the bitmap, I wrote this code:
_bmp _is the main Bitmap for the entire Activity. _splitYCoord _is the Y coordinate of the splitting point.
I create 2 smaller bitmaps. mBmp1 is the upper part of the larger bitmap and mBmp2 is the bottom part. Each part’s height is determined by the splitYCoord.
Pass the bitmaps to the next Activity
After I have the 2 bitmaps, I want to start the next Activity and put them on top of its layout. That way the user will see Activity A’s layout where in fact we already moved on to Activity B.
At first, I wanted to pass them as Extras of the Intent, it’s possible since Bitmap is Parcelable . The problem is the bitmaps are too large to transport in an Intent, due to size limitation for IPCs. This is the error I got:
There are few workarounds for this, including writing the bitmaps into a file and then read them back on the other end. I found the easiest and fastest way will be to just keep them as data members in a common area. I created a static class to contain the bitmaps and all the logic to create and animate them. More on that later.
Show the bitmaps on Activity B
After I start Activity B, disabling any default activity animation using overridePendingTransition(), I create 2 ImageViews to contain the previously created bitmaps, and presenting them on screen. I do that before calling _setContentView() _to avoid seeing Activity B’s layout ahead of time.
The ImageViews are added directly to the Window of the activity. This allows the ImageViews be on top of the soon-to-be inflated layout and gives more flexibility when deciding the location of each one of them on the screen.
The gravity states where we’ll put our layout in the window. Since we calculated the X,Y position of the bitmaps relative to the top of the screen – we’ll set the gravity to TOP.
Animate the bitmaps
After we created and positioned the bitmaps on Activity B, we can call setContentView() to inflate the Activity’s layout. After the layout is inflated we can start the animation that pushes the 2 bitmaps outwards, thus revealing the Activity’s layout.
The animation is a simple TranslationY animation that moves every ImageView outside the screen, each one to a different directions. I use hardware acceleration (read my last blog post to learn more about HW accelerated animations) and I do some cleaning after the animation is finished or canceled (removing hardware layer, removing the ImageViews from the window etc.).
How to use my Animation
So I was debating with myself what’s the best way to make it easier to use, without limiting the developer too much. The easiest way was to create a base Activity that the developer can extend and the actions will be taken care of without too much effort. The reason I didn’t do that is because I **HATE **extending from other base activities just to get some more features. If your application has it’s own base activity it can be very frustrating if every library will demand you to extend from their own branded Activity.
That’s the reason I created a simple class with static methods that do all the work. here’s the API:
In order to use it, just call this from Activity A when you want to animate to Activity B:
And call this on Activity B’s onCreate():
No need to extend anything, just 3 simple calls to static methods.
The code supports API14+ but it can easily be converted to work on older devices using NineOldAndroid.
The repository is here:
Use it, Fork it, Extend it.
There are more things that can be done to extends it:
- Vertical splitting – Let the activity split also to the sides.
- Split the activity to more than 2 parts
- You name it!