Layouts, Attributes, and you

Knowing how that picture in your mind (or that wireframe a designer gave you) translates into actual layouts and Views is one of those key skills that every Android developer can benefit from.

What is a layout?

Just looking through developer.android.com for layouts, you’ll find plently of classes ending in ‘Layout’. What do they all have in common? They’re all subclasses of ViewGroup — a View that supports adding child Views (commonly referred to as children).

As you might expect, one of the main responsibilities of a ViewGroup is laying out those children: picking how large each View is (the ‘measure’ phase) and placing the Views within the ViewGroup (the ‘layout’ phase).

Note: that isn’t to say that’s all a ViewGroup is responsible for. It can certainly have its own custom behavior, draw things itself, add its own children. Toolbar, for example, has quite a bit of built-in functionality, in addition to supporting child Views.

So it should come as no surprise that if you’re looking for a certain way that child Views are laid out, picking the right layout is going to play a big part. The wrong layout may make a certain pattern impossible or perform horribly, while another layout may simplify things.

Layout_ Attributes

Now just like any View, a ViewGroup can use XML attributes, like LinearLayout’s android:orientation, to change how they lay out their children, but these are global changes that affect every child. To change things on a child-by-child basis, layouts use a different mechanism in the form of layout_ attributes, which are added to child Views. These attributes are different because layout_ attributes are instructions for the parent ViewGroup, not for the View itself. Let’s take an example from a previous pro-tip I wrote:

If you look at Toolbar, you won’t find anything about layout_scrollFlags. Nor will you find anything if you look at AppBarLayout. Those layout_ attributes are actually stored in LayoutParams (specifically, in this case, the AppBarLayout.LayoutParams subclass). Each View, when attached to a parent, has its own LayoutParams that serves as a storage place for basically anything the parent ViewGroup wants to keep track of. By default, that’s just a width and height (that layout_width and layout_height you’ve seen on practically every View), but each ViewGroup has the opportunity to declare new attributes in their own subclass of LayoutParams (as helpfully described in the documentation).

Note: This is also the #1 reason why inflating a view from XML without including a parent (i.e., passing null as the root in LayoutInflater.inflate()) is a horrible idea — without any parent, there’s no one to parse and create a proper LayoutParams object, effectively meaning all of those attributes are thrown away — probably not what you want.

Common Android Layouts

Just knowing about LayoutParams and the layout_ attributes explained in the documentation might be enough to help you choose the right layout for you, but a quick summary can’t hurt.

LinearLayout

However, even with that single focus, it still has a trick up its sleeve with the layout_weight attribute, which allows a child to expand its size to fill the remaining space — useful if you have a few wrap_content elements and a few others that need as much space as possible.

FrameLayout

FrameLayout acts quite differently compared to LinearLayout: here all children are drawn as a stack — overlapping or not. The only control on positioning is the layout_gravity attribute — pushing the child towards a side or centering it within the FrameLayout.

They also contain one of most exciting features: aspect ratio support, making it possible to declare only a single dimension (either height or width) and basing the other on a fixed aspect ratio. This even works if one dimension is wrap_content or match_parent!

GridLayout

GridLayout was introduced in Ice Cream Sandwich back in 2011, but is also available as part of its own Support Library (to support back to API 7). Designed to place items in arbitrary rows and columns and supporting the same weights as LinearLayout, it allows you to flatten your view hierarchy considerably while avoiding some of the complex arrangements of elements that affects the performance of RelativeLayout.

Unlike most layouts, GridLayout does not require layout_height and layout_width for each View — columns and rows (and hence their contained children) grow and shrink as needed based on the Alignment of each. I’d strongly recommend reading over the GridLayout.LayoutParams documentation and the blog post (noting that it was written before GridLayout gained the layout_weight attribute) if you’d like to delve into this component.

Attaching a Behavior to a view either by using the @DefaultBehavior annotation on the class, using the layout_behavior attribute, or using setBehavior() allows that Behavior to intercept just about everything before the underlying View: measurement, layout, nested scrolling, touch events, changes to specified dependent Views, and window insets.

Layouts, layouts, layouts

Even with just the few layouts described above, you can build a rich UI that is both performant and easy to maintain. Next time you are struggling with a particular layout, consider taking a step back and seeing if there’s an easier way to do thing by using a different layout or if building your own custom layout (and taking on the responsibilities that entails — not insignificant) is the best approach.

Either way, use the right layout and the right layout_ attributes to #BuildBetterApps