Android Core

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!

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 ViewGroup and attaching Views to 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 content
  • fill_parent (or match_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:

Figure 1
Figure 1

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:

Figure 2
Figure 2

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:

Figure 3
Figure 3

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:

Figure 4
Figure 4

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:

Figure 5
Figure 5

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 View
  • layout_toRightof: the left edge position of this View is to the right of a specific View
  • layout_below: the top edge of this view is below to a specific View
  • layout_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 container
  • layout_alignParentRight: the right edge of this view matches the right edge of its container
  • layout_alignParentTop: the top edge of this view matches the top edge of its container
  • layout_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:

Figure 6
Figure 6

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:

Figure 7
Figure 7

2.4. FrameLayout

FrameLayout is a special layout that we will cover in more detail later. We saw different layout manager that implements some specific strategies to place views on the UI. FrameLayout is used when we want to display dynamically a view. It is very useful when we use Fragments.


 

2.5. GridLayout

This is the last layout manager that we cover in this article. It is very similar to TableLayout and it was introduced since Android 4.0. This layout manager disposed its views in a grid form, but respect to the TableLayout it is easier to use. GridLayout divides the screen area into cells. Its children occupy one or more cells.

2.6. Layout and multiple screen support

A special consideration is needed when we want to support multiple screen size. When we want to build an app for multiple devices, we have to implement several layouts. In Android, we have different directories and there we can implement the layouts. The default layout is the one under res/layout. We can provide some size qualifier that we append to the layout:

  • small
  • large
  • xlarge

Moreover, we can even provide the screen orientation:

  • land
  • portrait

So, for example, if we want to create a layout for an extra large screen, we create a directory under res called layout-xlarge and here we implement our layout.

If we want to provide a layout for landscape mode we create another dir, under res, called layout-land land and so on. In Android 3.2 there were introduced other size qualifiers to better adapt the layout to the screen size. If you want to have more information you can look here.

3. Fragments

By now, we have seen how we can build UIs using layouts and components. In this situation, we have an Activity with its layout and components. We know how we can adapt our layout for multiple screens but this technique sometimes is not enough, especially when we want to support tablets and smartphones. We have talked about this already, and we know that the smartphone screen size is very different from the screen of a tablet. Moreover, it is necessary to make the UI more dynamic and flexible in a large screen tablets. For these reasons, Android 3.0 (API level 11) has introduced the fragments concept.

What is a fragment? A fragment is a piece of user interface in an Activity. We can combine multiple fragments to create complex UIs. Each fragment has its own lifecycle and we can manage the lifecycle of each fragment independently. A fragment exists just inside an Activity that acts as its container. When we add a fragment inside a layout, it lives inside the ViewGroup that represents the layout. A fragment is a very powerful component that helps developers to create dynamic UI and support, at the same time, multiple screen size.

A fragment can be seen as a reusable piece of code with its own interface. It is crucial to differentiate when we need to use fragments and when we can use just “simple” layouts. Well, as said before, there are some situations where simple layouts, even if they are adapted to the screen size, are not enough.

A classic example is an app that has a list of contacts where, when a user clicks on a contact, the app shows the contact’s details. In a smartphone we can move from one activity to another showing a different layout, list and details. However, in a tablet this behavior would result in a poorly appealing app that does not use all the screen size available. In the tablet, we would like to have a list and the details at the same time on the screen.

For example, in a smartphone we would have:

Figure 8
Figure 8

while in a tablet we would have:

Figure 9
Figure 9

In this case, fragments help us. We can create two fragments: one that handles the contact list and another one the contact details. So we have:

Figure 10
Figure 10

while in a tablet:

Figure 11
Figure 11

3.1. Fragment lifecycle

Now that we know when we should use fragments, we need to know how they work before using them. A fragment has its own lifecycle as an Activity has, but it is more complex in comparison to the activity lifecycle. Moreover, a fragment lives only inside an Activity that acts as its container. Below, the fragment lifecycle is shown:

Figure 12
Figure 12

As we can see, this lifecycle has more states in comparison with the activity lifecycle. Moving from the top to the bottom, we have:

  • onInflate: This method is called only if we define fragment directly in our layout using the tag. In this method we can save some configuration parameter and some attributes define in the XML layout file.
  • onAttach: This method is called as soon as the fragment is “attached” to the “father” activity and we can use this method to store the reference about the activity.
  • onCreate: It is one of the most important steps, our fragment is in the creation process. This method can be used to start some thread to retrieve data information, maybe from a remote server.
  • onCreateView: It is the method called when the fragment has to create its view hierarchy. During this method we will inflate our layout inside the fragment. During this phase we cannot be sure that our activity is still created so we cannot count on it for some operation.
  • OnActivityCreated: In this method, we are notified when the “father” activity is created and ready for use. From now on, our activity is active and created and we can use it when we need.
  • onStart: Here we do the common things as in the activity onStart, during this phase our fragment is visible but it isn’t still interacting with the user.
  • onResume: This method is called when the fragment is ready to interact with user. At the end of this phase our fragment is up and running!

Then, it is possible that the activity might be paused and so the activity’s onPause is called. Well, in this case the onPause fragment method is called too. After that, it is possible that the OS decides to destroy our fragment view and so the onDestroyView is called. After that, if the system decides to dismiss our fragment, it calls the onDestroy method. Here we should release all the active connections because our fragment is close to shutting down. Even during the destroy phase, it is still attached to the father activity. The last step is to detach the fragment from the activity and it happens when the onDetach is called.

3.2. How to use fragments

Once we know the fragment lifecycle, we need to know how we can create a fragment and how we attach it to the Activity. Creating a fragment is very simple, we have just to extend an Android class called android.app.Fragment . We can suppose, we want to create a fragment called Fragment1. In this case we have:

public class Fragment1 extends Fragment {
}

In this way, we have created a fragment. In this class we can override callback methods so that we handle different states in the fragment lifecycle. By now, we can suppose that this fragment has a very simple layout:

<LinearLayout 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="Test Fragment1" />

</LinearLayout>

In this case this layout just has a TextView component that writes a text on the screen. If we want to “attach” this layout to our fragment we can do it in the onCreateView method, because according to the fragment lifecycle, this method is called when the fragment creates its view hierarchy. So in our Fragment1 class we can override the onCreateView method and implement our logic:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
	Bundle savedInstanceState) {	
	View v = inflater.inflate(R.layout.text_frag, container, false);
	return v;
}

In this method, we inflate our fragment layout called text_frag (the one shown above). Now we have created the fragment and we need to attach it to the Activity that holds it, because we know that a fragment exists only inside an Activity. We can do this operation in two ways:

  • declaring the fragment inside the activity layout
  • declaring a place holder inside the activity layout and attach the fragment inside the activity

In the first case, this operation is static, meaning we attach the fragment permanently to the activity. In the second case we can manage fragments at runtime, so we can replace a fragment with another one for example.

If we declare the fragment inside the layout we have to use the fragment tag.

Moreover, we have to specify the full qualified class name. For example, if the Fragment1 class is under the package com.swa, for the activity layout we will have:

<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" >

    <fragment
        android:id="@+id/f1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.sswa.Fragment1" />

</LinearLayout>

Running the example above, we will get us:

Figure 13
Figure 13

A bit more complex is the second option where we want to manage fragments dynamically.

3.3. FrameLayout and FragmentManager

When we want to handle fragments dynamically inside our Activity we cannot use fragment tag in the activity layout but we have to use a place holder that we will “fill” with the fragment UI. In this case, the activity layout becomes:

<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" >

    <FrameLayout
        android:id="@+id/fl1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

In the Activity, we have to use a component that helps us to handle fragments. This component is called FragmentManager. Using this component we can add, remove, replace fragments at runtime and also to find fragments.

Note that before doing any kind of operations on a fragment, we need to activate a transaction. Using the FragmentManager we can create a transaction, start it and commit it at the end, when the fragment operation is done. So in the Activity we have:

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main_din);

		// Create fragment
		Fragment1 f1 = new Fragment1();
		FragmentManager fm = getFragmentManager();
		FragmentTransaction ft = fm.beginTransaction();
		ft.add(R.id.fl1, f1);
		ft.commit();
	}

}

3.4. Fragment Activity communication

Often we need to exchange information between a fragment and the activity that holds it. We need to consider that we can re-use fragments with different activities, so we cannot bind the fragment to a specific activity.

Inside a fragment, we can retrieve the activity instance using the getActivity() method. If we want to exchange data, it is a good practice to create a callback interface inside the fragment and require that the activity implements it.

For example, we can suppose to add a button to the fragment1 UI and when the user clicks the button we want to pass this information to the activity. The first thing we need to do is to modify our fragment layout:

<LinearLayout 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="Test Fragment Dynamic" />

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click me" />

</LinearLayout>

The second step is to create an interface like this:

public class Fragment1 extends Fragment {
	public static interface FragmentListener {
		public void onClickButton() ;
	}
}

Now that we have the interface, our activity has to implement it:

public class MainActivity extends Activity implements
		Fragment1.FragmentListener {
	@Override
	public void onClickButton() {
		// Handle here the event
	}
}

Now we have to check, in the fragment class, if the activity implements the interface so that we can notify the event, when it occurs. The best place to do it, remembering the fragment lifecycle, is in the onAttach method, so we have:

public class Fragment1 extends Fragment {

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		if (!(activity instanceof FragmentListener))
			throw new ClassCastException();

	}
}

We are, finally, ready to notify the event when the user clicks on the button and we can do it in the onCreateView method:

public class Fragment1 extends Fragment {

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View v = inflater.inflate(R.layout.text_frag, container, false);
		Button b = (Button) v.findViewById(R.id.btn1);
		b.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				((FragmentListener) getActivity()).onClickButton();
			}
		});
		return v;
	}
}

3.5. Inter fragment communication

When developing an android app using fragments, we not only need to exchange data between the fragment and the activity that contains it, but we also need to exchange information between two fragments. In this case, the best practice is to avoid a fragment to fragment communication, so that we can guarantee that fragments are decoupled.

If we want to exchange data in ths manner, we can pass it from the first fragment to the activity, using the method described before, and then from the activity to the destination fragment. In this way, we can be sure that our fragments can be re-used in different situations.

3.6. Multiple screen support using fragments

At the beginning of this article, we explained when we should use fragments. We specified that they are very useful when we want to support different screen sizes, for example when we build an app that runs on a smartphone and on a tablet too. In this case just using layouts is not enough.

It is time ti explore how to create an UI that works on a tablet and a smartphone. In order to achieve this, we can revisit the example described before. In this case, we can suppose that when a user clicks on the button then the app shows a message. If we have a smartphone, when user clicks on the button then we move to another activity that shows the message, while if we use a tablet we want to show this message on the same screen.

First things first, we code a second fragment with a very simple layout that just shows a message and we call this fragment Fragment2:

public class Fragment2 extends Fragment {
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {	
		View v = inflater.inflate(R.layout.frag_message, container, false);
		return v;
	}
}

The layout is:

<LinearLayout 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="Hello world" />

</LinearLayout>

If the app runs on a smartphone, then the layout is similar to the one described previously, while some special consideration has to be done in the case of a tablet. We can suppose, for simplicity, that tablet has a landscape layout, so we can create, under res directory, a new directory called layout-land:

 <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="horizontal" >

    <FrameLayout
        android:id="@+id/fl1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <FrameLayout
        android:id="@+id/fl2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

Notice that we have two FrameLayouts in this case. The “heavy” work is done by the Activity. If in the current layout attached to it there is no FrameLayout with id equal to f2, then we have a single fragment layout, meaning we are using a smartphone, otherwise we are using a fragment.

In the callback method in the activity we have:

import wkkwc.com;

@Override
	public void onClickButton() {
		// Handle here the event
		if (findViewById(R.id.fl2) != null) {
			// We have dual fragment layout
			
			Fragment2 f2 = (Fragment2) getFragmentManager().findFragmentById(R.id.fl2);
			
			if (f2 == null) {
				// Fragment2 is not inizialized
				f2 = new Fragment2();
				FragmentTransaction ft = getFragmentManager().beginTransaction();
				ft.replace(R.id.fl2, f2);
				ft.commit();
			}
			
		}
		else {
			// Single fragment layout
			Fragment2 f2 = new Fragment2();
			FragmentTransaction ft = getFragmentManager().beginTransaction();
			ft.replace(R.id.fl1, f2);
			ft.commit();
		}
	}

Running the app, we have in the smartphone:

Figure 14
Figure 14

Figure 15
Figure 15

while, if we run the app on a tablet, we have:

Figure 16
Figure 16

Figure 17
Figure 17

4. Download the Source Code

This was a lesson of how to create Android Layouts with ViewGroups and Fragments. You may download the source code here: AndroidLayout.zip

Francesco Azzola

He's a senior software engineer with more than 15 yrs old experience in JEE architecture. He's SCEA certified (Sun Certified Enterprise Architect), SCWCD, SCJP. He is an android enthusiast and he has worked for long time in the mobile development field.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Jim
Jim
7 years ago

Very good explanation. Thank you

Back to top button