Writing a Weather App for Android: The UI

For the past couple of days, I’ve been writing a weather app that pulls information from the OpenWeatherMap API. I’ve got a model and an API for accessing the data. Now it’s time to write some UI code to show the data.

Android has a concept called an Activity. An Activity is basically “something the user does” – and that includes all the screens for your app. My app has only one screen right now – I’m going to display the weather for my home in Seattle. The best way of developing a screen is to develop the UI first and use dummy data for all the information on the page, then – later on – hook up the controls to the real data source. This allows you to rapidly iterate on the UI without worrying about hitting API limits for the services you are using or even if you have network access issues.

All mobile development have the same concepts for developing screens. There is some sort of abstracted UI definition – XML for Android, Storyboards for iOS, XAML for Xamarin, and JSX for React Native. The XML is for Android is relatively easy to understand, especially if you are familiar with XAML or JSX. You have a layout manager and then a bunch of components like text boxes, icons and so on within the layout. In addition, there is an Activity java class (default for the first screen is called MainActivity) that implements the view logic for what is displayed.

For my page, I have two specific files:

  • app/src/main/res/layout/activity_main.xml is the UI definition file.
  • app/src/main/com/shellmonger/weather/MainActivity.java is the associated Activity.

Let’s take a look at each one in turn, starting with the XML file. I am thinking that my app will have three specific areas:

  1. At the top will be the city name and the timestamp of the weather.
  2. In the middle will be the meat of the weather report – the condition icon and text, temperature, and so on. When loading, this will become a progress spinner.
  3. At the bottom will be an advertising spot. I won’t do advertising in this version, so I’ll produce a “fake” banner advertisement.

For the ad, I will copy a 320×50 graphic image that I download from an image archive and include it in my app. All mobile banner ads are either 300×50 or 320×50 – this requirement is from the IAB (Interactive Advertising Bureau) and they set the standard.

Looking at my layout requirements, I am going to use a RelativeLayout, which is a powerful layout mechanism where each component of my layout is given a position relative to the others. There are other layout mechanisms that I could use. For example, I could use a cascading set of LinearLayout components to produce the same effect.

For the textual components, I’m going to use a TextView component. This can be used for everything since my weather icons are going to be pulled in from a truetype font. Let’s see what this looks like so far:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/top_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#3F51B5"
    android:padding="20dp">

    <TextView
        android:id="@+id/city_field"
        android:text="Seattle, WA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:textColor="#FFFFFF"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/updated_field"
        android:text="Never"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/city_field"
        android:layout_centerHorizontal="true"
        android:textColor="#FFFFFF"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textSize="13sp" />

    <TextView
        android:id="@+id/weather_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:textColor="#FFFFFF"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textSize="90sp"
        />

    <TextView
        android:id="@+id/current_temperature_field"
        android:text="70.0°F"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/weather_icon"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:textColor="#FFFFFF"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textSize="50sp" />

    <TextView
        android:id="@+id/details_field"
        android:text="Light Drizzle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/current_temperature_field"
        android:layout_centerHorizontal="true"
        android:textColor="#FFFFFF"
        android:textAppearance="?android:attr/textAppearanceMedium"
        />
</RelativeLayout>

A couple of notes here:

  • Every component gets an id field – this is important so you can refer to it in the MainActivity.java file.
  • You can specify text as I have here, but it is an anti-pattern. I will want to extract these strings into a resource file later on. I’m only including it to see what the display will look like.
  • I haven’t included the weather icons yet – most on that later.

You can build this UI using the text editor or the graphic designer in Android Studio. I tend to drag and drop the components I want onto the page, but then I will adjust with the text editor later. If you are following along, you can just copy-and-paste the code into the text editor, then take a look at it in the graphical designer to see what it is going to look like in the finished product.

Specifying Strings in the UI

I mentioned that adding text directly into the layout file is an anti-pattern. So what is the appropriate pattern? Well, fortunately, Android Studio helps you. Put your cursor on the line in question, click the lightbulb icon and select Extract string resource. This will bring up a dialog for you to correct. I don’t want the string resource for the city to be called seattle_wa. Instead I want it to be called default_city_name. Similarly, I am going to rename all the other fields. This will update a file app/src/main/res/values/strings.xml with the following contents:

<resources>
    <string name="app_name">Yet Another Weather App</string>
    <string name="default_city_name">Seattle, WA</string>
    <string name="default_updated">Never</string>
    <string name="default_temperature">70.0°F</string>
    <string name="default_details">Light Drizzle</string>
</resources>

It will also adjust the android:text fields with the appropriate links to these values. For example:

    <TextView
        android:id="@+id/city_field"
        android:text="@string/default_city_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:textColor="#FFFFFF"
        android:textAppearance="?android:attr/textAppearanceLarge" />

You can do these adjustments manually as well. I like to take advantage of the IDE when possible for these things – it saves me time and prevents me from making stupid typos.

Including Weather Icons

There isn’t a standard font for displaying weather icons, so I have two choices. Firstly, I can create a PNG or SVG file for each weather condition. Alternatively, I can use a weather font and display the appropriate character in that font. There are a lot of weather icon sets out there. The first match was one of the better ones – sadly unmaintained. In this case, however, how much maintenance is actually required?

Integrating the weather icons involves:

  1. Download the font file (it’s available on the GitHub repo).
  2. Place the font file in the app/src/main/assets/fonts directory. You are likely going to have to create this directory.
  3. Add the following to the MainActivity.java file (highlighted changes)
package com.shellmonger.weatherapp;

import android.support.v7.app.AppCompatActivity;
import android.graphics.Typeface;
import android.widget.TextView;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    TextView weatherIcon;
    Typeface weatherFont;

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

        weatherFont = Typeface.createFromAsset(getAssets(), "fonts/weathericons-regular-webfont.ttf");
        weatherIcon = (TextView)findViewById(R.id.weather_icon);
        weatherIcon.setTypeface(weatherFont);
    }
}

If you take a look at the accompanying CSS file, you can translate the code for the icon you want to display into its text form. In this case, I want the default to be “cloudy”, which is (according to the website) wi-day-cloudy. Look up this CSS class in the file and I find this translates to \\f002. I can place the HTML entity version of this in the XML file as my text content (and convert to a strings value). I won’t be able to see the font in action until I run the app because the typeface is not installed by default. In Android O (and Android Studio 2.4) there is an alternate way of specifying fonts. However, I am not expecting to use that for some time since it won’t be backwards compatible. Running the app makes it look like this:

Next Steps

There is still a fair bit to do in this app. The next step is to wire up the WeatherManager API and the UI, which has some complexities. Until then, the code thus far is on my GitHub repository.