You have probably already used
FrameLayout (doc) for what it’s named, adding a decoration around other content element. It can actually be much more than that and is probably one of the most versatile container of all.
The secret of
FrameLayout is how it layouts its children. Although normally designed to contain one item, it will happily stack up other element on top of each other. Thus
FrameLayout is essentially a way to manipulate the Z-order of views on the screen.
This is super useful for a couple of UI tricks from HUD-like elements to sliding panels to more complex animated transitions. In this post we will see an example for each of those.
As I mentioned,
FrameLayout will automatically stacks its children on top of each other. This makes it really easy to implement overlay or HUD element in your interface.
The idea is to wrap the part of the UI into an outer
FrameLayout instance. Generally this will be the root element of your layout. You can then add below the main layout definition the other elements you want to overlay.
These elements will generally have a fixed size or be set as
wrap_content. You can then place them at the right position on screen using the
layout_margin XML attributes.
In the following screenshot, my main content is a Google Map view. I have then added two overlays above it, one that replicate a toast message (center-bottom corner) and a
TextView displaying the last time the data was loaded (upper-right corner).
This is simply achieved with the following layout:
Being just normal views, you can manipulate the overlays as usual. For instance you will probably want to implement some sort of animations for transition between visibility states if the overlay is transient.
Sliding panels (aka drawers, aka fly-out menus) are the rage these days. They mainly consist of a panel that is placed, depending on the desired spatial effect, above or below another piece of content. Again this falls back to placing elements using a
The trick then is to use the
translationY properties of the panel
View to move the element back and forth.
View also exposes the
OffsetLeftAndRight methods (working on display list directly) but those are not as easy to animate. Indeed, with the translation properties you can directly use
The panel is initially “hidden” by setting its
translationY property to its height (i.e. top side forced to the bottom of the screen). We create the initial appearing effect with the following code piece inside the panel class:
A common idiom of those panel is also to draw a shadow on the edge where they meet the content. This can be implemented very efficiently by overriding the
DispatchDraw method of the sliding panel
View and drawing a black linear
GradientDrawable at the right place (use the
Translate method on
Canvas for the right positioning).
With the move to a
Fragment world, everything becomes just another
View to manage and thereby removing the need for
When you think about the different “screen” of you application as individual fragments it becomes very easy to manipulate them in fun ways. Notably, being just managed
View objects, you can apply any of the animations techniques you are already familiar with to implement very nifty transition effects.
At the core of such system, you will find again the same idea of a central, expanded
FrameLayout surface where your
Fragment are added.
The idea is to pile the
Fragment views on top of each other like a deck of card. The current screen is thus the first card of the pile and application transitions are then simply how you want the middle cards to be put on top.
I’m using this analogy of the card deck because the method we are going to use here is
View.BringToFront. This method will move the view at the last position inside its parent array. The end result of that operation of course depends on the type of container but in the case of
FrameLayout it will make the
View be drawn on top.
Note that this method doesn’t force a relayout which is fine for
FrameLayout since children individual bounds aren’t dependent on each other but in the case of e.g.
LinearLayout you will have to call
RequestLayout for the modification to happen.
We will assume the layout of our main
Activity is a single full-size
FrameLayout container. Our application is composed of two screens represented as two fragments.
Initially, we create and attach directly those fragments to our content frame, hiding the second one to get into an initial state:
When we want to change the
Fragment that is shown, we will make sure that both views are on top of the drawing pile with the current one being on top of the other using
BringToFront. We then swap the two fragments in a
To add a bit of woosh, we are using customs animations to mark the transition between the two fragments to get this result:
These animations need to be defined in an XML resource using either property animator (aka from Android.Animations) if you are using framework
Fragment or using view animations (aka from Android.View.Animations) if you are using
Fragment from the support package.
To register custom animations, we call the
SetCustomAnimations method on
FragmentTransaction like so:
The first animation will be run on any fragment added/attached/shown while the second animation will be played on fragments hidden/detached/removed. Both are ran at the same time but you can use the animation
startOffset attribute to delay one or the other.
For instance in my example, my
frag_slide_out animation is defined like so:
Everything I have presented here is part of a big refresh to Moyeu. You can view the full source code on the GitHub repository at garuma/Moyeu.