Barbarian Meets Coding
barbarianmeetscoding

WebDev, UX & a Pinch of Fantasy

12 minutes read

Android Wiki

This article is part of my personal wiki where I write personal notes while I am learning new technologies. You are welcome to use it for your own learning!

I have taken most of this notes as I’ve been tinkering with Android following the different articles in Android’s developer documentation

Getting Started with Android

  1. Download Android Studio
  2. Create a project with an empty activity.

When you create a basic project you have:

  • MainActivity which contains the main activity of your app. Each part on an app typically maps to an activity in Android terms.
  • activity_main.xml which represents the view or UI for that activity
  • AndroidManifest.xml which describes your app and its components
  • Two build.gradle files, one for the project and one for the app module. Each module has its own build.gradle file. You’ll typically configure the module’s build.gradle file to configure how your app gets built. More info

Creating a Basic Hello World

We’ll start by creating a basic hello world that will consist of a TextBox and a button. When you click on a button we’ll show a dialog that will say Hello whatever_you_type.

The UI of Android apps resemble a tree of layouts and widgets. Layouts are ViewGroup objects that can contain other layouts or widgets (View objects). Layout are just invisible containers used to lay out child views on the screen. These child views can be other layouts or widgets which represent actual visible elements on the screen like text or buttons. They way in which we represent this hierarchy of layouts and widget is through XML.

If you open your main layout file activity_mail.xml you’ll be able to see that you have a simple UI defined by the following xml code:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.vintharas.myapplication.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Which means that your UI is composed of two items:

  • A layout of type ConstraintLayout
  • A TextView (a view that shows some text) with the text Hello world

The ConstraintLayout is a new type of layout that allows you to set the position of a child view based on constraints in relation to the parent layout and sibling view. This type of layout allows you to reduce the complexity in your views by reducing the number of nested layouts you need to create a UI.

Remove the TextView from your UI and we’ll start by adding an EditText widget that will allow us to introduce our own input. You can type in the XML element directly and you’ll get help from Android Studio:

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

Or alternative you can use the WYSIWG editor and drag and drop a “Plain Text” widget. This last option is pretty nice because it’s a great way to find out about the attributes that you can use. For instance, if I drag and drop the component in the UI it will result in the following code:

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name"
        tools:layout_editor_absoluteX="31dp"
        tools:layout_editor_absoluteY="16dp" />

The tools:layout_editor_absoluteX="31dp" and tools:layout_editor_absoluteY="16dp" reflect the place where I dropped the TextView. What we want to do instead is to take advantage of the constraints available to this special layout. We can use the designer to achieve that:

  1. Click on the TextView to select it,
  2. You’ll see circle appears on each border of the input,
  3. if you drag and drop to a border of the layout you’ll create a constraint

You can do that or type:

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name"
        android:layout_marginLeft="16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="16dp" />

As you can see from the last 4 attributes we have setup two constraints for the TextView:

  • The left border of the input is 16dp from the left border of the layout
  • The top border of the input is 16dp from the top border of the layout

Next we add a button and we setup the following constraints:

  • Let the button be 16dp to the right of the text input
  • Set a constraint to align the baseline (the text) of these two widgets

Now we have a UI with a EditText and a Button that should look like this:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.vintharas.myapplication.MainActivity">

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name"
        android:layout_marginLeft="16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="16dp" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintLeft_toRightOf="@+id/editText"
        android:layout_marginLeft="16dp"
        app:layout_constraintBaseline_toBaselineOf="@+id/editText" />
</android.support.constraint.ConstraintLayout>

The next step is to write suitable messages to invite the user to type his name:

  • The input should say Type your name
  • The button should say Say hello!

You can update the directly using the android:text attribute like this:

    <EditText
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Type your name"
        android:layout_marginLeft="16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="16dp" />

A better way to do it is to collect all the strings within your application within a resource file, that is, a file that contains key/value pairs to identify and collect all the user facing strings of your application. Think of it as a way to remove string duplication from your app, adding an extra level of indirection that allows you to update text faster, translate to different languages, etc.

The way you handle this in Android is via xml resource files. The default app that we created has such a file created from the beginning named strings.xml:

<resources>
    <string name="app_name">My Application</string>
</resources>

We can add a couple of new strings here:

<resources>
    <string name="app_name">My Application</string>
    <string name="edit_name">Type your name</string>
    <string name="button_say_hello">Say hello!</string>
</resources>

And now we can refer to them in our UI xml file. You can either use the designer and select the appropriate text or use the following convention within the xml:

    <EditText
        android:text="@string/edit_name"
        // etc
        />

That is, @string/edit_name where the string refers to the type of resource and the edit_name is the key that identifies the resource.

The next step is to wire everything up so when we click on the button something happens. In order to do that we will update the code of our MainActivity to include a new sayHi method that will contain the logic needed to:

  1. get text typed into input
  2. display Hello whatever you typed in a new activity

This method will look like this:

package com.vintharas.myapplication;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {
    public static final String SAY_HI_MESSAGE = "com.vintharas.myapplication.SAY_HI_MESSAGE";

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

    public void sayHi(View view){
        // create intent that represents our "intent"
        // to launch another activity
        Intent intent = new Intent(this, SayHiActivity.class);

        // Get text we wrote in the input
        EditText editText = (EditText) findViewById(R.id.editText);
        String name = editText.getText().toString();

        // add text as part of the intent
        intent.putExtra(SAY_HI_MESSAGE, "Hello " + name + "!" );

        // start activity using the intent we just created
        startActivity(intent);
    }
}

We can break it down as follows: First we create an intent that represents our intent to launch another activity that will contain the message we want to display Hello XXXXX:

// create intent that represents our "intent"
// to launch another activity
Intent intent = new Intent(this, SayHiActivity.class);

Then we obtain the text the user typed in the text input by:

  1. Getting a reference to the input using findViewById
  2. Using the getText method on the input to get the actual text
EditText editText = (EditText) findViewById(R.id.editText);
String name = editText.getText().toString();

After that we add that information to the intent we created earlier as an extra and launch the new activity:

// add text as part of the intent
intent.putExtra(SAY_HI_MESSAGE, "Hello " + name + "!" );

// start activity using the intent we just created
startActivity(intent);

Now we have a sayHi method that can launch a new activity with the Hello XXXX message but it isn’t connected to anything, that is, it doesn’t matter how many times we click on the button nothing will happen because we haven’t connected the sayHi method with the button. We can connect both together using the designer, select the button, look for an onClick handler and select the sayHi method. Now when you click on the button the MainActivity will execute the sayHi method.

There’s one thing remaining though, we don’t have a SayHiActivity yet. We can create it using File -> New -> Android -> Activity. Select Empty Activity, name it SayHiActivity and place a TextView in it. Update the code of the new activity to the following and we’re done!

package com.vintharas.myapplication;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class SayHiActivity extends AppCompatActivity {

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

        // Get intent that started this activity
        Intent intent = getIntent();
        String message = intent.getStringExtra(MainActivity.SAY_HI_MESSAGE);

        // Show message
        TextView textView = (TextView) findViewById(R.id.textView);
        textView.setText(message);
    }
}

Or we’re almost done. If you run the example you’ll be able to see that you can click on the button, see the message and… that’s it. You’re trapped for eternity seeing the message and can’t go back to the original view. How can you fix that? You can update your android manifest to include the following information for the SayHiActivity:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.vintharas.myapplication">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <!-- HERE !!!!! -->
        <!-- the parentActivityName is the bit we're interested in -->
        <activity android:name=".SayHiActivity"
                  android:parentActivityName=".MainActivity">


            <!-- The meta-data tag is required if you support API level 15 and lower -->
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value=".MainActivity" />
        </activity>
    </application>

</manifest>

How to Support Different Devices, Sizes, Form Factors, Locales?

The Android ecosystem is huge! There’s multitude of devices with very different sizes, form factors and features, and it’s widely used throughout the world in very disparate locales. This means that there’s a lot of things that you need to take into consideration when developing your app.

Supporting Different Locales

In the previous sample app you saw how we extracted the strings within our app into a resource file called strings.xml. This is a great practice for localizing your apps (making them available in different languages).

To add support for diverse locales you create additional directories within the res folder for each separate locale using a naming convention:

<resource type>-b+<language code>[+<country code>]

For instance, the equivalent for the values folder that contains the strings.xml resource file for swedish would be:

values-b+SV

As you saw in our previous example app, you can get access to these resources in different ways depending on whether you’re writing code or XML:

  • in code by using R.<resource type>.<resource key> (for instance R.id.editText)
  • in XML by using @<resource type>/<resource key> (for instance @string/edit_name)

You can find more information about localizing your in the Android developer documentation

Supporting Different Screen Sizes

TODO

Supporting Different Versions of Android

TODO

Building Dynamic UIs with Fragments

*Fragments represent a behavior or a portion of user interface in an Activity. You can think of them as a modular section within an activity or a sub-activity that you can reuse across different activities. They have their own lifecycle and their own input events. If you have a web development background, it is useful to think of Fragments as web components.

You can find more information in the Android developer documentation with sample code

Developing Apps with Data

TODO

References


Jaime González García

Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.Jaime González García