Writing a simple Android app: 2. Creating the main screen

STEM Speed Ramp

The STEM Speed Ramp

Understanding the Structure of Android Apps

Before diving straight into the development, it occurred to me that it might be a good idea to have a quick scan through the Android documentation to see how the application lifecycle works and what I need to do to get the thing going. The relevant Android documentation can be found here:

http://developer.android.com/training/basics/activity-lifecycle/index.html

Android apps are built around a set of ‘Activities’ that move through a set of states over course of their lifetime as shown in the diagram below.

The Android Activity lifecycle

The Android Activity lifecycle (source Google)

Of the states described above, the app will spend the majority of time in the Resumed, Paused or Stopped states and it’s the transitions between these that need to be looked at carefully because I need to make sure that the app doesn’t crash if the students re-orientate the screen or switch to another app. If they switch between apps, I also don’t want them to lose any work they’ve already done so while the app is in use, data will need to be persisted. When the app is first started however, we don’t want the data in there still from the previous class so when the app moves to the destroyed state, we want the data to be deleted.

For this app I’m going to need two different activities, one to allow the students to configure the ramp and another to display the splash screen that must be displayed on start-up.

I started off by creating the main ramp configuration screen. Having right-clicked on the namespace in Package Explorer I selected Android and Android Activity and clicked Next. I then selected ‘Blank Activity’ from the options and clicked Next. As I forgot to check the ‘Launcher Activity’ on the way through the screens, I had to manually add the launder information to the activity in AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.siemens.stem"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name="com.siemens.stem.MainActivity"
            android:label="@string/title_activity_main" >
            
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Note that I also changed the minSDKVersion to 10 which is fine for an app running on Android 2.3.3 (see http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels).

Setting up the main screen

I started by opening the file activity_main.xml which opened in a screen editor window. In the portrait view I added in the tab control and the various labels, text fields and buttons. The tab control has three tabs labelled Easy, Hard and Settings. For more advanced students, the ‘Hard’ tab requires them to do more of the configuration work while the ‘Easy’ option is designed for younger students who may not be able to cope with some of the more advanced concepts. The ‘Settings’ tab is where the ramp settings are configured.

Having added the Tab control to the form I then opened activity_main.xml in the XML view and edited some of the values. The resulting activity_main.xml file looked like this:

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

    <TabHost
        android:id="@android:id/tabhost"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" >
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" >
    
            <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" >
            </TabWidget>
    
            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="match_parent"
                android:layout_height="match_parent" >
    
                <LinearLayout
                    android:id="@+id/easyTab"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" >
    
                    <TextView
                        android:id="@+id/textView1"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="Easy"
                        android:textAppearance="?android:attr/textAppearanceLarge" />
    
                </LinearLayout>
    
                <LinearLayout
                    android:id="@+id/hardTab"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" >
                    <TextView
                        android:id="@+id/textView1"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="Hard"
                        android:textAppearance="?android:attr/textAppearanceLarge" />
                
                </LinearLayout>
    
                <LinearLayout
                    android:id="@+id/settingsTab"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" >
                    <TextView
                        android:id="@+id/textView1"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="Settings"
                        android:textAppearance="?android:attr/textAppearanceLarge" />
                
                </LinearLayout>
            </FrameLayout>
        </LinearLayout>
    </TabHost>
</RelativeLayout>

While I had the dialog editor open I made a couple of other quick changes too. As I’m targeting a device that supports Android version 2.3.3 I updated the Android version in the dialog editor to 10. I also know that I’m targeting a device that has a 7” screen with a resolution of a measly 480×800 pixels. The closest I could find in the available options in the dialog editor was a 5.1” screen with the 480×800 resolution so I decided to use that for laying out my controls.

Setting up the tab control

I started by giving the TabHost an ID so that we can get at it from the code. In the outline view, I selected the TabHost and opened the ID property setting the name to tabHost.

I then opened the MainActivity class file and added the following code.

package com.siemens.stem;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // get a reference to the tab host
        TabHost tabHost = (TabHost)findViewById(R.id.tabhost);
        
        // set it up
        tabHost.setup();
        
        // create the easy tab
        TabSpec spec1 = tabHost.newTabSpec("Easy");
        spec1.setContent(R.id.easyTab);
        spec1.setIndicator("Easy");

        // create the hard tab
        TabSpec spec2 = tabHost.newTabSpec("Hard");
        spec2.setContent(R.id.hardTab);
        spec2.setIndicator("Hard");
        
        // create the settings tab
        TabSpec spec3 = tabHost.newTabSpec("Settings");
        spec3.setContent(R.id.settingsTab);
        spec3.setIndicator("Settings");
        
        // add them to the tab host
        tabHost.addTab(spec1);
        tabHost.addTab(spec2);
        tabHost.addTab(spec3);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
}

Running that code up on the device allowed me to see the tab control and after I added a label on each of the tab linear layouts just to show which one was visible, I could see it was switching between the tabs just fine.

I was keen though to use resources for the tab text. To do this  I first created the required resource strings in the strings.xml file.

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">STEM Ramp Configurator</string>
    <string name="title_activity_main">Enter Ramp Settings</string>
    <string name="action_settings">Settings</string>
    <string name="tab_caption_easy">Easy</string>
    <string name="tab_caption_hard">Hard</string>
    <string name="tab_caption_settings">Settings</string>

</resources>

While I was in there I also updated some of the other strings that I wanted to customise. Getting the strings out of the resources and using them turns out to be easy as you’d expect. It’s just a case of calling the function getString() and passing in the ID of the string you want to display so I updated the MainActivity class as follows.

package com.siemens.stem;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // get a reference to the tab host
        TabHost tabHost = (TabHost)findViewById(R.id.tabhost);
        
        // set it up
        tabHost.setup();
        
        // create the easy tab
        TabSpec spec1 = tabHost.newTabSpec("Easy");
        spec1.setContent(R.id.easyTab);
        spec1.setIndicator(getString(R.string.tab_caption_easy));

        // create the hard tab
        TabSpec spec2 = tabHost.newTabSpec("Hard");
        spec2.setContent(R.id.hardTab);
        spec2.setIndicator(getString(R.string.tab_caption_hard));
        
        // create the settings tab
        TabSpec spec3 = tabHost.newTabSpec("Settings");
        spec3.setContent(R.id.settingsTab);
        spec3.setIndicator(getString(R.string.tab_caption_settings));
        
        // add them to the tab host
        tabHost.addTab(spec1);
        tabHost.addTab(spec2);
        tabHost.addTab(spec3);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}

I then did a bit of a test run and all appeared to be well.

The main screen tab controls

The main screen tab controls

Adding the Controls to the Screens

The next step was to add the controls to the first of the tabs. I did this by selecting the ‘easy’ tab’s linear layout, adding a table view and dropping textViews and editTexts into it. I defined the strings for the captions and units in the strings.xml file and set some label width and heights and editText widths in the dimens.xml file which I then set in the relevant property grids.

The updated dimens.xml now looked like this:

<resources>

    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
    <dimen name="max_setting_label_width">200dp</dimen>
    <dimen name="max_setting_label_height">50dp</dimen>
    <dimen name="max_setting_textview_width">150dp</dimen>

</resources>

I also added a ‘Send’ button to the bottom of the grid view and set its caption.

Running up the application and showing the ‘easy’ tab now shows the display as shown below.

Controls on the 'Easy' tab

Controls on the ‘Easy’ tab

Having sorted out the controls on the first tab, I then went on to do the others. I couldn’t figure out a way to display the other tabs and do the layouts until I found the embarrassingly simple answer in a Stack Overflow article. Simply moving the layout you want to edit to the top of the list of tab pages makes it visible then you can rearrange them once you’re done.

Main screen configuration tabs

Main screen configuration tabs

Once all of the controls had been added to the tab pages I ran it up on the device and checked through to make sure I had the correct field lengths and other constraints. I also made sure the edit boxes had recognisable names so I can get at the data they’ll contain easily. I used the best fitting ‘input type’ for the edit boxes. Integer values are restricted to ‘number’ values without decimal points, float values allow the entry of a decimal point and are set to ‘numberDecimal’. The only non-numeric field is the one that allows entry of the ramp host or IP address.

In the next article I’ll discuss how I added the functionality behind the screens. In the meantime if you’d like to see the finished code, I’ve uploaded it to GitHub here: https://github.com/jpmasters/stem-ramp-configurator.

Advertisements
2 comments
  1. wasim said:

    Can I open new Activity new New Tab.. ?

    • Hi Wasim, thanks for stopping by. I’m not sure of your question. Are you asking if you can create an Activity that is just one tab on a tab control?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: