Android UI: Layouts with View Groups and Fragments
This article is part of our Academy Course titled Android UI Design – Basics.
In this course, you will get a look at the fundamentals of Android UI design. You will understand user input, views and layouts, as well as adapters and fragments. Check it out here!
Table Of Contents
1. Overview
In the previous article, we talked about Views and we explained how to build user interfaces using Views. We discovered different kinds of Views, with different controls. Android provides several common controls and using them we can build appealing user interfaces. We saw some useful patterns we can implement when we create UIs so that we can guarantee consistency to our app interface.
In this article we want to explore how we can organize such views and how these views can be placed on the screen. In other words, we will analyze in detail layout managers, or simply layouts.
2. Layout overview
When we create our app interface, we use some special view that acts as container. These special views control how other views are placed on the smartphone/tablet screen. Android provides a collection of Layout Managers and each of them implements a different strategy to hold, manage and place its children. From the API point of view, all the Layout managers derive from the ViewGroup class. There are some layouts that place their children horizontally or vertically, and others that implement a different strategy. We will analyze them in details later in this article.
In Android, layouts can be nested so we can use different layouts for different areas of our interface. However, please ve aware that it is not advisable to create too complex layouts, because this can affect the overall app performance. We can declare a layout in two ways:
- Using XML: In this case, using an XML file we “describe” how our user interface looks like. We define the elements (views and sub layouts) that appear in the user interface. At the same time, we define their attributes, as we saw in the previous article.
- At runtime: In this case, we code our layout instantiating our
ViewGroupand attachingViewsto it. We can manipulate their properties programmatically setting their attributes.
We can use both approaches in our app. We could, for example, use XML to create the user interface and assign to its Views some properties. At run time, we can find (or lookup) this Views and ViewGroup (layout) and change their properties programmatically. We could for example, have a View with red background and at runtime we change it to green color. Android is very powerful and flexible from this point of view.
Using XML, we somehow decouple the presentation from the code that handles its behavior. In XML, the UI description is external to the source code, so theoretically we could change the presentation, meaning just the XML file, without touching our source code. This is the case when, for example, we want to adapt our user interface for multiple screen dimensions. In this case, we define different layouts having the same name but in different directories, and the system chooses the one that best matches the screen dimensions. This is the standard approach taken by Android in order to handle multiple screen sizes. Moreover, we will see later that we can use another technique based on Fragments. If we use XML, it is possible to use draw the layout and debug it easily.
From the API point of View, each ViewGroup defines a nested class called LayoutParameter that holds some parameters that define size and position for each views belonging to the ViewGroup. All ViewGroups have in common two parameters called width and height (or layout_width and layout_height) that every View must define. These two parameters represent the width and the height of the View. We can specify a numeric value or more often we can use two constants:
wrap_content, meaning that the dimension of the view will depend on the actual contentfill_parent(ormatch_parent), meaning that the view has to become as big as its parent holding it
A view in Android is a rectangle, and the view location is expressed as a pair of coordinates left and top. These two values determine the View position inside its ViewGroup.
Another important View property inside a Layout is padding, expressed with four values (let, top, right, bottom). Using padding we can move the content of the View.
Android provides several standard layout managers:
- Linear Layout
- Table Layout
- Relative Layout
- Frame Layout
- Grid Layout
2.1. LinearLayout
This is the simplest Layout manager. This layout disposes its children vertically or horizontally depending on the orientation parameter. To define this layout in XML, we can use:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
The orientation attribute is the most important attribute because it determines how views are placed. It can assume two values: horizontal or vertical. In the first case, the views are placed horizontally and in the second case they are placed vertically. There are two other parameters that affect the position and the size of the views. They are gravity and weight.
The gravity parameter behaves like the alignment. So if we want to align a view to the left side we can set the gravity left or right, if we want to align it to the right. The corresponding attribute in XML is layout_gravity. If we create an app, using the layout shown below:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:text="Left" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:text="Right" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Center" />
</LinearLayout>
and run it, we have:
Another important parameter is weight (or layout_weight in XML). Using the weight parameter we can assign an importance value to a View respect to the others. The View with higher importance value is more important than Views with lower values. In other words, Views with higher weight value consume more space than other Views. For example, see this layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="left"
android:text="Left" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="right"
android:text="Right" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center"
android:text="Center" />
</LinearLayout>
In this case, we used layout_weight and we gave more importance to the TextView with right text. Running the app, we have:
Another important aspect we have to consider is the difference between android:gravity and layout_gravity. Even if they look very similar, they have a different meaning. android:gravity is an attribute used by the View, while layout_gravity is a parameter used by the container.
2.2. TableLayout
This is layout manager that disposes its children in a table, grouping them in rows and columns. For example, using the layout shown below, we create two different rows holding two cells.
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TableRow>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 1" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 2" >
</TextView>
</TableRow>
<TableRow>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 3" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 4" >
</TextView>
</TableRow>
</TableLayout>
We used as TableLayout child the TableRow, which represents a row inside the table. Running an app with this layout we have:
We can even use different cell numbers for different rows like the example shown below, where in the second row we have three cells:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TableRow>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 1" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 2" >
</TextView>
</TableRow>
<TableRow>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 3" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 4" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 5" >
</TextView>
</TableRow>
</TableLayout>
In this case, the first row has an empty cell. Running the example we have:
It is not required to use TableRow, because we can use all the components that extend the View class. In this case, this component will have the same width as the table. For example:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is a row!" >
</TextView>
<TableRow>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 1" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 2" >
</TextView>
</TableRow>
<TableRow>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 3" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 4" >
</TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Cell 5" >
</TextView>
</TableRow>
</TableLayout>
In this case, we did not use TableRow but we used TextView as the first TableLayout child and we specified that the TextView width should be as big as the content. If we run an app with this layout, we have:
You can notice that even if we specified that the TextView should be as big as the content, it still occupies the entire row.
2.3. RelativeLayout
This is the most flexible layout in Android. This layout manager uses a policy where the container places its Views relative to other Views. We can implement, using this layout manager, very complex UI structures.
RelativeLayout implements some view attributes that we can use to place the View. There are attributes that control the position of the View respect to other Views:
layout_toLeftof: the right edge position of this View is to the left of a specific Viewlayout_toRightof: the left edge position of this View is to the right of a specific Viewlayout_below: the top edge of this view is below to a specific Viewlayout_above: the bottom edge of this view is above to a specific View
There are other parameters that are used to place the view respect to its container:
layout_alignParentLeft: the left edge of this view matches the left edge of its containerlayout_alignParentRight: the right edge of this view matches the right edge of its containerlayout_alignParentTop: the top edge of this view matches the top edge of its containerlayout_alignParentBottom: the bottom edge of this view matches the bottom edge of its container
There are some other parameters that can be used and you are advised to have a look at the documentation. For example:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/t1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text1" />
<TextView
android:id="@+id/t2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/t1"
android:text="Text2" />
<TextView
android:id="@+id/t3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/t1"
android:layout_toRightOf="@id/t2"
android:text="Text3" />
</RelativeLayout>
In the example above, we placed the TextView with id t2 below the TextView with id t1, the TextView with id t3 is places to the left of t2 and below t1. Running the example we have:
Let us suppose we want to add another TextView to the layout above and this TextView has to be placed in bottom right corner, so we have:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/t1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text1" />
<TextView
android:id="@+id/t2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/t1"
android:text="Text2" />
<TextView
android:id="@+id/t3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/t1"
android:layout_toRightOf="@id/t2"
android:text="Text3" />
<TextView
android:id="@+id/t4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="Text4" />
</RelativeLayout>
Running the example, we have:
















Very good explanation. Thank you