Android UI: Understanding Views
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 our previous article, we introduced some basic concepts about Android UI patterns and we saw how we can create consistent UI following those patterns. In this article, we want to explore more about Views and how we can use them to build great user interfaces. This article will cover concepts about Views and Adapters. Developing a user interface in Android can be quite simple since we can describe them using XML files and the system will do the heavy work converting those in real user interface components, placing them according to the screen size and resolution.
The Android UI framework provides some basic UI components, that are called controls or widgets, helping developers while coding and building an app.
2. Views
The View class is the basic class that all the components extend. A View draws something on a piece of screen and it is responsible to handle events while user interacts with it. Even the generic ViewGroup class extends View. A ViewGroup is a special View that holds other views and places these views following some rules. We will see that Android provides some specialized views that helps us to handle text, images, user inputs, buttons and so on.
All these views have some key aspects in common:
- All views have a set of properties: These properties affect the way the view is rendered. There is a set of properties common to all views, while there are some other properties depending on the type of view.
- Focus: The system manages the focus on each view and depending on the user input, we can modify and force the focus on a specific view.
- Listeners: All views have listeners which are used to handle events when the user interacts with the view. We can register our app to listen to specific events that occur on a view.
- Visibility: We can control if a view is visible or not and we can change the view visibility at runtime too.
A view property is a key value pair that we can use to customize the view behavior. The property values can be:
- a number
- a string
- a reference to a value written somewhere else
The first two options are trivial, the third is the most interesting one because we can create a file (always in XML) that holds a list of values and we can reference it in our property value. This is the best approach to follow especially when we use themes and styles or we want to support multi-language apps.
One of the most important property is view id: this is a unique identifier for the view and we can look up a specific view using this id. This is a “static” property meaning we cannot change it once we have defined it.
When we want to set a view property, we have two ways to do it:
- using XML while we define our view
- programmatically
For example, let’s suppose we want to define a TextView (it writes a text). In XML we have:
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
As you can notice, we defined an id called textView1, some other properties that have a string values and another property called android:text that reference a value written somewhere else.
We could do the same thing using just code lines:
TextView tv = new TextView(this);
tv.setText("blabla");
These two approaches are equivalent; however, the first one in XML is the preferred one. There is a correspondence between XML view property and the Java method: usually for each XML property there is a set method that we can use in our code. You can look here to have more information about this correspondence. In XML, a view property is called attribute.
Two properties that play an important role are layout_width and layout_height. These two properties define how large and how tall should be the view. We can use two predefined values:
MATCH_PARENTWRAP_CONTENT
With MATCH_PARENT value, we mean that we want our view as big as its parent that holds it, while with WRAP_CONTENT we specify that our view must be big enough to hold its content.
There is another option: using a numeric value. In this case, we specify the exact measure of our view. In this case, the best practice suggests using dp unit measure so that our view can be adapted to different screen density.
Talking about view listeners, this is a well-known approach when we want to be notified about some events. A listener in Java is an interface that defines some methods, which our listener class must implement so that it can receive notifications when a specific event occurs.
The Android SDK has several listener types, so different interfaces can be implemented. Two of the most popular are: View.OnClickListener, View.OnTouchListener and so on.
Android provides several standard components and of course if we cannot find a component that fulfills our needs, we can always implement a custom view. Using a custom view, we can define what our view will draw on the screen and its behaviour. We can even define custom properties that affect the view’s behavior. In this case, we have to extend the basic View class and override some methods.F
2.1. TextView component
This is one of the simplest components. We use it when we want to show a text on the screen. We have seen it before:
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
where android:text holds the text we want to show. It has some attributes we can use to specify how we want to show the text:
android:fontFamily– It is the font-family.android:shadowColor– It is the shadow color.android:shadowDx– It is the shadow x axis offset.android:shadowDy– It is the shadow y axis offset.android:textStyle– It is the style (bold, italic, bolditalic).android:textSize– It is the text size.
Below are some examples of TextView:
and the corresponding XML is:
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android UI" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android UI"
android:textStyle="italic" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android UI"
android:textColor="#FFFFFF"
android:textSize="15dp"
android:textStyle="bold" />
2.2. ImageView component
This component is used to show an image on the screen. The image we want to show can be placed inside our apk or we can load it remotely. In the first case, our image has to be placed under the res/drawable directory. Base on what we discussed in our previous article, we already know we have to keep in mind that our app can run on multipe devices with different screen density.
To better support different screen densities, we can create different images with different resolutions. The res/drawable directory is the default directory where the system looks if it cannot find an image with the right density for the screen. Generally speaking, we have to create at least four different image with different resolutions, in fact we can notice in our IDE that there are at least five directories:
drawable-ldpi(not supported any more)drawable-mdpi(medium dpi)drawable-hdpi(high dpi)drawable-xhdpi(extra-high dpi)drawable-xxhdpi(x-extra-high dpi)drawable
An important thing to remember is the following: the images must have the same name. Once we have it, we can use the ImageView in this way:
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/robot" />
where we referenced the image, called robot.png, using @drawable/robot (without the extension). The result is shown below:
If the image has to be loaded remotely, there are some considerations to keep in mind while doing it. First, you should consider that loading an image from a remote server is a time consuming operation and you should do it in a separate thread so that you can avoid ANR (Application Not Responding) problem. If you want to keep things simple you could use an AsyncTask or you can use the Volley lib. The second thing to remember is that you cannot set the image in an XML file, but you can define only the ImageView without referencing an image. In this case, you can set the image using:
img.setImageBitmap(your_bitmap)
where your_bitmap is the image you have loaded remotely.
2.3. Input controls
Input controls are components that the user can interact with. Using a component like that, you can, for example, give to the user the chance to insert some values. The Android UI framework provides a wide range of input controls: text field, input field, toggle buttons, radio buttons, buttons, checkbox, pickers and so on. Each of them has a specialized class and we can build complex interfaces using these components.
There is a list of common components, below, with the class that handles it:
| Component | Description | Class |
Button | This one of the most used components. It can be pressed by a user and when pressed we can launch an action. | Button |
TextFields | This is an editable text and we give to the user the chance to insert some data. EditText is the classic input field while AutoCompleteTextView is a component we can use when we have a pre-defined list of result and we want the system to complete some words inserted by user | EditText AutoCompleteTextView |
CheckBox | This is an on/off component. We can use it when we want user to select one or more choices. | CheckBox |
Radio buttons | Very similar to the checkbox except for the fact that the user can select only one item. | RadioGroup RadioButton |
Toggle button | On/off component with a state indicator. | ToggleButton |
Pickers | Component that helps the user to select one value using up/down buttons or gesture. Usually we use DatePicker and TimePicker | DatePicker TimePicker |
Table 1
We want to focus our attention on a Button component that this is widely used in Android UI. First, we have to define our UI using XML file:
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:id="@+id/Btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Click here!" />
</RelativeLayout>
Notice that in the button id we used @+id meaning we are adding a new id and the android:text attribute is the string we want to show on the button. You could use a reference instead of writing text directly. If we create a simple app using this layout and run it we will have:
We will see later how we can catch UI events so that we can handle user clicks. We can consider a bit more complex interface:
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:id="@+id/Btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Click here!" />
<CheckBox
android:id="@+id/checkBox1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/Btn"
android:layout_below="@+id/Btn"
android:layout_marginTop="29dp"
android:text="CheckBox" />
<ToggleButton
android:id="@+id/toggleButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/checkBox1"
android:layout_below="@+id/checkBox1"
android:layout_marginTop="20dp"
android:text="ToggleButton" />
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/toggleButton1"
android:layout_centerVertical="true"
android:ems="10"
android:hint="Your name here" >
<requestFocus />
</EditText>
<Switch
android:id="@+id/switch1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/toggleButton1"
android:layout_alignBottom="@+id/toggleButton1"
android:layout_alignParentRight="true"
android:text="Switch" />
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/editText1"
android:layout_below="@+id/editText1"
android:layout_marginTop="24dp" >
<RadioButton
android:id="@+id/radioButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/editText1"
android:layout_below="@+id/editText1"
android:layout_marginTop="40dp"
android:text="Option 1" />
<RadioButton
android:id="@+id/radioButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/radioButton1"
android:layout_below="@+id/radioButton1"
android:text="Option 2" />
</RadioGroup>
</RelativeLayout>
In this interface, we used almost all the UI components and if we run the app we have:
3. UI Events and Listeners
By now, we have created Android user interfaces but we have not considered how to handle events that the user generates when he interacts with our app.
Listeners are an important aspect when developing UIs in Android. Generally speaking, when an user interacts with our app interface, the system “creates” some events. Each view that builds our interface is capable of generating events and providing the means to handle them.
From an API point of view, if we look at a View class, we can notice there are some public methods; these methods are called by the system when some events occur. They are called callback methods, and they are the way we can capture the events that occur while an user interacts with our app. Usually these callback methods starts with on + event_name.
Every View provides a set of callback methods. Some are common between several views while others are view-specific. Anyway, in order to use this approach, if we want to catch an event, we should extend the View and this is clearly not practical. For this reason, every View has a set of interfaces that we have to implement in order to be notified about the events that occur. These interfaces provide a set of callback methods. We call these interfaces as event listeners. Therefore, for example, if we want to be notified when a user clicks on a button we have an interface to implement called View.onClickListener.
Let us consider the example above when we used a Button in our UI. If we want to add a listener, the first thing we have to do is to get a reference to the Button:
Button b = (Button) findViewById(R.id.btn);
In our Activity there is a method called findViewById that can be used to get the reference to a View defined in our user interface. Once we have the reference, we can handle the user click:
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// here we handle the event
}
});
As you can see we used a compact form and you can notice the callback method called onClick. This method is called by the system when a user clicks on our button, so here we have to handle the event. We could obtain the same result in a different way: we can make our Activity implement the View.OnClickListener interface and implement onClick again. Running the app we have as a result:
We can use the same technique if we want to listen to different events.
4. UI Development
Previously, we shortly introduced the Activity concept. It is time to better explain its function. An Android UI cannot exist if there is not a container that holds it. This container is called Activity and it is one of the most important concepts in Android. Every app we will create has at least one Activity and usually, more complex apps have several activities that interact each other exchanging data and information using Intents. An Activity takes care of creating a window where we place our UI.
We can, now, build a complete app that uses the UI components shown above and mix them. Let’s suppose we want to create an app that asks the user’s name and it has a button to confirm it.
First thing is to create a layout. We assume you already know how to create an Android project. If not, please refer to our “Android Hello World” article. In Android (i.e. using Eclipse as IDE) we have to place the XML files under res/layout. If you look carefully, you can notice that there are different sub-directories with different names and extensions. By now, it is enough that we add our files under res/layout (the default directory). Other dirs will be used depending on the type of the device, so that we have the chance to optimize our UI according to the screen size and orientation.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/edt"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Please insert your username" />
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:ems="10" />
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="16dp"
android:text="Confirm" />
</RelativeLayout>
activity_main.xml
Next we have to create an activity. If we use an IDE, we will notice that it has already created a default class (in Eclipse it is called MainActivity.java). Looking its source code, there is a onCreate method. Here, the first thing we do is to “attach” our UI to the activity:
setContentView(R.layout.activity_main);
Next we can handle the click event: when user clicks, we can retrieve the EditText value and show a message:
// Lookup Button reference
Button b = (Button) findViewById(R.id.btn1);
// Lookup EditText reference
final EditText edt = (EditText) findViewById(R.id.editText1);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String txt = edt.getText().toString();
Toast.makeText(MainActivity.this, "Hello " + txt, Toast.LENGTH_LONG).show();
}
});
Running the app we obtain:












