Android Development - (2) Fundamentals 👽

What do I need to install to get started?

You can read my previous post Android Development - Getting Started.

How do I make an app?

The core elements of an app are:

android studio text view

android studio design view

The rest can be discussed in more detail as we focus on each aspect.

How does an app do stuff?

The main activity is launched when a user clicks on the app icon.

A task is when two or more activities are chained together. We use an intent to pass a message between activities to achieve this.

How is our project organised?

Typically, assets are organized into different directories based on their purpose.

android directory structure

Activity

An activity represents a single user screen, and is a single defined thing that the user can do. You extend android.app.Activity or a subclass.

Configure the manifest

Each activity you create must have an entry in AndroidManifest.xml.
Android Studio does this for you when you create a new activity using it’s wizard (as below).

new wizard

The only required attribute for the activity element is android:name, which specifies the class name of the activity.

<manifest ... >
<application ... >
<activity android:name="design.roboleary.conversion.ConversionActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application ... >
...
</manifest >

One activity needs to be marked as a main activity, so you can launch your app. We use an intent filter to do this. We will speak about this in more detail when we discuss intents.

Lifecycle events

Skillfully managing activities allows you to ensure that:

So, an activity has callback methods that are called at different stages of it’s existence to enable us to manage it appropriately. Below is a diagram outlining when the methods are called depending on the state of the activity.

activity states

Static resources

You should always externalize app resources such as images and strings from your code, so that you can maintain them independently.

You should also provide alternative resources for specific device configurations, such as different screen resolutions, and group them in specially-named resource directories. At runtime, Android chooses the appropriate resource based on the current configuration.

Once you externalize your app resources, you can access them using resource IDs that are generated in your project’s R.java.

Grouping resources

Directory Resource Type
animator/ XML files that define property animations.
anim/ XML files that define tween animations. (Property animations can also be saved in this directory, but the animator/ directory is preferred for property animations to distinguish between the two types.)
color/ Defines a list of colors.
drawable/ Bitmap files (.png, .9.png, .jpg, .gif) or XML files that are compiled into drawable resources.
mipmap/ Drawable files for different launcher icon densities.
layout/ XML files that define a user interface layout.
menu/ XML files that define app menus.
raw/ Arbitrary files to save in their raw form. To open these resources with a raw InputStream, call Resources.openRawResource() with the resource ID.
values/ XML files that contain simple values such as: strings, integers, and arrays. By convention, each type is stored in a separate file.
font/ Font files with extensions such as .ttf, .otf, or .ttc, or XML files that include a element.
xml/ Arbitrary XML files that can be read at runtime by calling Resources.getXML().

Exercise: Make a temperature conversion app

We want to make an app that will convert a temperature from celsius to farenheit, and the other way around.

Create a new project

Property Value

Application Name

Temperature Conversion

Package name

design.roboleary.conversion

Minimum SDK

Latest Android release

Template

Empty Activity

Activity

MainActivity

Layout

activity_main

Backwards Compatibility (AppCompat)

false (not selected)

Create our strings in static resources

Open res/values/strings.xml, and add the String definitions to the file as described as below. These are the values that we will we use for labels in our layout.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Temperature Conversion</string>
<string name="celsius">Celsius</string>
<string name="fahrenheit">Fahrenheit</string>
<string name="calc">Calculate</string>
</resources>

Create the layout

This is roughly what we want.

layout

Open our layout file res/layout/activity_main.xml in
the “text view”, and delete everything.

We want a simple layout, so we add a LinearLayout element as the root, which organizes everything in a horizontal or vertical line. We can want everything to
be organized vertically, so we set this property android:orientation="vertical". You
can switch to the “design view” to add the views to this layout then.

A simple way of organizing the view components is to drag
and drop them onto the “Component Tree view”. So you can
see the order we want them arranged in. We make the radio buttons children of the radio button group.

component tree

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



<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/editText1" />


<RadioGroup
android:id="@+id/radioGroup1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/editText1"
android:layout_below="@+id/editText1">


<RadioButton
android:id="@+id/radio0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="RadioButton" />


<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RadioButton" />

</RadioGroup>

<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/radioGroup1"
android:layout_below="@+id/radioGroup1"
android:layout_marginTop="22dp"
/>


<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/textView1"
android:layout_below="@+id/textView1"
android:layout_marginTop="22dp"
android:text="Button" />

</LinearLayout>

Edit view properties

For the radio buttons:

  1. We can change them to have more meaningful ids, celsius and farenheit are good.
  2. We can use the string values from our static resources by making a reference like this android:text="@string/celsius" for the first radio button (highlighted below), and do similar for the second radio button.
  3. Make the first radio button checked with android:checked ="true"

edit properties

For our EditText:

  1. We set android:inputType="numberSigned|numberDecimal". This changes the keyboard that is used to input the value.
  2. We can also change the ID
    android:id="@+id/inputValue".
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/inputValue"
android:inputType="numberSigned|numberDecimal" />

For the button:

  1. We assign the string value android:text="@string/calc".
  2. Change the ID to calcButton.
  3. Add android:onClick="onClick" to reference
    the onClick method, which we will create in our
    MainActivity to respond to the user clicking the button.
<Button
android:id="@+id/calcButton"
android:onClick="onClick"
...
android:text="@string/calc" />

This is what the complete layout looks like.

edit properties

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



<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/inputValue"
android:inputType="numberSigned|numberDecimal" />


<RadioGroup
android:id="@+id/radioGroup1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/editText1"
android:layout_below="@+id/editText1">


<RadioButton
android:id="@+id/celsius"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/celsius" />


<RadioButton
android:id="@+id/farenheit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/farenheit" />

</RadioGroup>

<TextView
android:id="@+id/outputValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/radioGroup1"
android:layout_below="@+id/radioGroup1"
android:layout_marginTop="22dp"
/>


<Button
android:id="@+id/calcButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@+id/textView1"
android:layout_below="@+id/textView1"
android:onClick="onClick"
android:layout_marginTop="22dp"
android:text="@string/calc" />


</LinearLayout>

Create an utility class

To do the conversion.

package design.roboleary.conversion;

public class ConvertUtil {
// converts to celsius
public static float convertFahrenheitToCelsius(float fahrenheit) {
return ((fahrenheit - 32) * 5 / 9);
}

// converts to fahrenheit
public static float convertCelsiusToFahrenheit(float celsius) {
return ((celsius * 9) / 5) + 32;
}
}

Update the MainActivity

We need to define the onClick method to handle the user
interaction:

package design.roboleary.conversion;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast;

public class ConversionActivity extends Activity {
private EditText text;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (EditText) findViewById(R.id.inputValue);
}

// this method is called at button click because we assigned the name to the
// "OnClick" property of the button
public void onClick(View view) {
switch (view.getId()) {
case R.id.calcButton:
RadioButton celsiusButton = (RadioButton) findViewById(R.id.celsius);
RadioButton fahrenheitButton = (RadioButton) findViewById(R.id.farenheit);
TextView output = (TextView) findViewById(R.id.outputValue);
if (text.getText().length() == 0) {
Toast.makeText(this, "Please enter a valid number",
Toast.LENGTH_LONG).show();
return;
}

float inputValue = Float.parseFloat(text.getText().toString());
if (celsiusButton.isChecked()) {
String result = String.valueOf(ConvertUtil.convertCelsiusToFahrenheit(inputValue));
output.setText("= " + result + " farenheit");
} else {
String result = String.valueOf(ConvertUtil.convertFahrenheitToCelsius(inputValue));
output.setText("= " + result + " celsius");
}
break;
}
}
}

References