Intro to Android Development

Objectives

The main goal of this lab is to become familiar with the Android development ecosystem, in particular:

  • creating a new Android project,
  • creating a virtual machine to test the project,
  • understanding the organization of the components of an Android application,
  • composing a basic graphical interface, and
  • adding its basic functional features.

We will see in the following lab how to make more complex interfaces.

Start Android Studio

In this lab, we will use Google’s official developer tool, Android Studio. This integrated development environment (IDE) is a modified version of IntelliJ, similar in concept to Eclipse.

This environment does not run correctly in the lab machines. If you have not already done so, download Android Studio and install it on your computer.

When we launch Android Studio, the home screen shows us some possible actions such as opening an existing project or creating a new project (“Start a new Android Studio project”). It is the latter that we will choose.

Create a first project

We will now create a first Android project. Our application will be a rudimentary currency converter. In its first version, there will be three labels, three text fields, and two buttons: convert and reset. Let’s get started.

Android Studio proposes a bunch of templates for different types of applications. For this exercise, we will create a new Empty Views Activity. Hint: Make sure you select Empty Views Activity. The plain “Empty Activity” template uses an alternative, declarative, way of defining Activities that is not covered in this exercise.

  • For the name of the application, enter a suitable name, such as “Money” for example.
  • For the package name, put something like “fr.enst.yourusername” (e.g. fr.enst.eagan). This package name is based on the reversed domain (e.g. fr.enst.eagan).
  • For the location of the project on the disk, choose a reasonable folder.
  • Make sure you choose the Java language. (If you know Kotlin, you are free to use that, but you will need to adapt the Java code in this exercise on your own.)

If everything went well, you should have a nice Android project.

Organization of an Android project

Let’s take a closer look at the project that Android Studio created for us. You may have noticed that there are one or two million files in our project, only some of which do we care about today:

  • app - this is where you’ll find all the contents of our app, including src and res, described below.
  • gradle - the tool used by AndroΓ―d to compile and package our applications. It’s a concept similar to the Makefiles you might have seen or will see in INF224.
  • src - this folder contains the source code for our application, including
    • MainActivity.java which is our main screen. Remember that an activity corresponds roughly to a screen in Android.
  • res - contains the non-code resources of the application
    • drawable-* - raw images to be composed in the interface. The different versions correspond to the resolution of the device.
    • layout - the interface descriptions in XML format, including our
      • activity_main.xml which describes the interface for MainActivity.java.
    • values - we ignore these files except for:
      • strings.xml which contains definitions of the strings we use in the interface.

The first thing that is a bit weird is this notion of separating strings in their own file. This approach allows us to easily create localizations and translations of the text in the project.

The layout/*.xml files are a bit like the designs we do in Qt Designer. It’s an XML file that describes the interface that will be displayed on the screen.

Test the app

The first thing we’d probably like to do is test our application. For the moment, it won’t do much, but as computer scientists, we are legally obligated to start with “Hello, World!”

Click the “Run” button, with its little green triangle. The first time we do this, Android Studio will ask us to create a virtual machine to emulate an Android device. Use the Device Manager to create a new virtual device. You can access the Device Manager by clicking on the right gutter on the screen or from one of the microscopic toolbar icons (it looks like a phone).

Running these virtual devices is resource intensive. I recommend that you choose an older device with a smaller screen size and lower resolution, such as the Pixel 2.

Then, you have to choose the version of Android that the virtual machine will run. What is important is that the version of the Android APIs is at least as recent (high) as what you chose when you created the project. If there is an already downloaded version (that doesn’t have a “Download” link) with a sufficiently high API, use it. Otherwise, make a choice which is β‰₯ the API of your project.

If all goes well, Android Studio will launch our new virtual machine. After some time, once the machine is started, you should see a nice Android screen that says “Hello World”.

Compose the interface

Open layout/activity_main.xml. Android Studio does offer us a graphical editor to compose the interface. It’s a very good tool, but to better learn what really happens, we will first use the XML interface. Click the Text tab at the bottom of the editor to switch to the XML view.

The interface we see corresponds to our “Hello World”, created automatically by Android Studio when we create a new project. First, let’s replace the outermost placeholder layout with our own:

    <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"
        tools:context=".MainActivity" >
        ...
    </LinearLayout>

Keep the Hello World TextView in that LinearLayout, but get rid of any app:layoutConstraint attributes. (They are for a more complicated type of layout that we will not talk about today.)

Let’s make sure we haven’t broken anything with out changes. Try running the app again. You should see Hello World! greeting you at the top of the screen. If not, try to see if you can figure out what’s going wrong until you get something that works. If you still can’t figure it out, that’s what we’re here for; just ask for some help.

Our first version of the interface will contain four rows: one for a label and text fields to enter the amount in euros, one to enter the exchange rate, one to indicate the equivalent value in dollars, and one for the two buttons.

To create these rows, simply put a layout inside the LinearLayout, creating a hierarchy of widgets. The LinearLayout we have already created will have a vertical orientation. To create the rows, we just need to put more LinearLayouts inside, but with a horizontal orientation. (Of course, there are other possible layouts).

The subsequent LinearLayouts (after the first one) will no longer need the xmlns:* and tools:context attributes. (They are similar to import statements in Java.) Get rid of them, and then change the orientation to horizontal. Also, we no longer want the height to be that of the parent. So change it to wrap_content, leaving us with:

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

Move the HelloWorld TextView to this row and change its text to @string/euros_label. If you try to launch the application, Android Studio will complain that this value is not set yet. The compiler is vigilant, but we are not slaves to the machine! We’ll define it later, when we want to, dagnammit. Then, add a text field:

    <EditText android:id="@+id/edit_euro"
            android:inputType="numberDecimal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="@string/euros_label" />

Note that we have given an android:id attribute to this item. This attribute will allow access to this widget in our java code. The + before id indicates that we define a new object with this id.

Test the interface again to make sure it works. Normally we’ll see that it doesn’t compile, because we haven’t defined the @string/euros_label yet.

Open the strings.xml file in res/values and add an entry for euros_label with the value “Euros” following the pattern of the other strings defined in the file.

Test again and debug if necessary.

Once you’re happy that it works, add another row for the exchange rate. Re-compile and run to test. Then add another rank for the result. Hint: to create an unmodifiable text field, use the attributes android:enabled="false" and android:focusable="false".

Test the application again.

Notice that the interface layout is not very good. Widgets are sized by the size of their content and not by the available space. There is another option, android:layout_weight which gives a relative weight to the widget in the layout. It’s like making a cocktail: the weight is relative to the other components (e.g. to make a delicious interface, make one part TextView for two parts EditText. Yum!). By default, a widget has a weight of 0. When you use an android:layout_weight for a dimension, you don’t want to calculate the size according to the contents, so you also set android:layout_width (or height, if necessary) to 0dp :

    <EditText android:id="@+id/edit_euro"
        android:inputType="numberDecimal"
        android:layout_weight="1""
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="@string/euros_label" />

In a new row, add the buttons. A button is written like this:

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button_convert"
        android:onClick="convert" />

The android:onClick attribute links the button to a method of the activity associated earlier with the tools:context attribute. We will define this method soon. But first, don’t forget to define the strings in strings.xml.

Implement the functionality

The callback method (“callback”) associated to a widget via android:onClick should be void and should accept only one parameter, the View that triggered the action:

    public void myMethod(View sender) { ... }

Implement the recall methods referenced in the layout. To retrieve a widget defined in the layout by its identifier, use:

    EditText editText = findViewById(R.id.widget_ID);

To find and modify the value of a text field, the methods editText.getText().toString() and editText.setText(blahBlahString) could be useful.

Improving the interface

Congratulations, your Android application works. If you still have time, think about improving the interface. Google offers good guides on the Android website:

What changes would you make ?

Next week, we’ll see how to display several screens with several activities.