Writing a simple Android app: 4. Adding styles and the splash screen

STEM Speed Ramp

The STEM Speed Ramp

Styling the app

Android apps are styled using stylesheets in a similar way to the cascading stylesheets used in web pages. In order to figure this step of the process out I referenced the android documentation at http://developer.android.com/guide/topics/ui/themes.html.

The first thing I did was to try the process out so I created a new stylesheet in my app resources values folder called rampstyles.xml. I used the Android XML Values file option from the new file dialog just in case there was something special required (there isn’t as far as I can tell). I then added a test base style to apply to the controls just to make sure the process I was following did what I thought it should do.

At this stage the content of the rampstyles.xml file looked as follows.

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <style name="rampcontrol">
        <item name="android:background">#ff0000ff</item>
    </style>    
</resources>

I applied the style through the dialog editor in Eclipse by selecting the bottom level linearLayout and setting the style to the new style name.

Setting the background test style

Setting the background test style

As expected the form turned blue so I went ahead and carried on with the style setting.

For this project I was provided with a splash screen by an external design agency so I decided that in order to give the app a consistent look and feel, I’d use a palette of colours for the forms that came from the image provided to me. Since I needed the image for the splash screen anyway, I started by importing it into the project.

App splash screen

App splash screen

First I created a drawable folder under the folder res and then I imported the splash screen image into it. I was then able to open the image and sample colours from it, turn them into hex values and use them in the styles.

I started by doing some easy stuff. I defined a background colour for the activity and another for the tab pages. These were defined as follows in the rampstyles.xml file.

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <style name="background">
        <item name="android:background">#FF136688</item>        
    </style>
    <style name="tabpage">
        <item name="android:background">#ff1a9ad6</item>
    </style>    
</resources>

They were then added to the layout to set the relevant colours.

Having done that I then decided to tackle the somewhat more complicated TabWidget. I searched all over the place to find a clear, concise way of doing it, eventually settling on the a great code snippets blog article here: http://maxalley.wordpress.com/2012/10/27/android-styling-the-tabs-in-a-tabwidget/.

The first thing I did was to add the additional layout and drawable xml files. I started off by using Max’s ones as they appear in his blog. Once that was done, I added a version of his getTabIndicator function to my MainActivity class and updated the setIndicator calls to use it. The resulting updates to the buildTabs method and the new getTabIndicator method are below.

/**
 * Set up the tab control with the correct pages.
 */
private void buildTabs() {
    // 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(this.getTabIndicator(tabHost.getContext(), R.string.tab_caption_easy));

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

/**
 * Inflates the tab indicator, sets the caption and returns the new view.
 * @param context
 * @param title
 * @return
 */
private View getTabIndicator(Context context, int title) {
    View view = LayoutInflater.from(context).inflate(R.layout.tab_layout, null);
    TextView tv = (TextView) view.findViewById(R.id.textView);
    tv.setText(title);
    return view;
}

Running up the app on the device now shows the new styles being applied. The next stage was then to get the styles to display with the colours that I wanted. In order not to have to keep entering the colour values and to make it easier to change things if required, I created a color.xml file in my values folder and added in the colours I picked from the splash screen image.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="background">#FF136688</color>      
    <color name="tabpagebackground">#FF1A9AD6</color>
    <color name="highlight">#FF58B5E0</color>    
    <color name="labeltext">#FFFFFFFF</color>    
</resources>

I then updated my rampstyles.xml file to use the new colours.

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <style name="background">
        <item name="android:background">@color/background</item>        
    </style>
    <style name="tabpage">
        <item name="android:background">@color/tabpagebackground</item>
    </style>    
</resources>

Running that up on the device showed that everything was still working so I went on to update some of the tab settings. For all but the unselected file, I removed the second item and updated the colour values to point at colours in color.xml. I left the second item in the unselected because I set the background colour to the same value as the root linearLayout and I wanted them to be framed. I also set the colour of the text in tab_layout.xml to white (@color/labeltext) so it’s the same as the rest of the text. Finally I updated tab_pressed.xml to use @color/highlight so it stands out a bit when the user presses on a tab.

The final bit of styling was to do something with the various buttons. In the specification, they were green and a lot smaller. I wasn’t keen to make them green because it looks horrible so I found another colour scheme, again based on the image provided for the splash screen. I then used an online button generator to create something a little more interesting and added the resulting images into my resources along with a selector (button_selector.xml) for the different images.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:drawable="@drawable/button_pressed" android:state_pressed="true" />
    <item android:drawable="@drawable/button_focused" android:state_focused="true" />
    <item android:drawable="@drawable/button_default" />
</selector>

Updating activity_main to get the buttons in there was a bit of a trauma but eventually I found a combination of layout types and settings that centred by button where I needed it. To get this to happen I had to convert the linear layouts for each tab page to vertical ones then after the TableLayout add a new RelativeLayout in each one to contain the appropriate button. The button settings then include android:layout_centerInParent=”true” to get the button to display in the centre. If you’d like to see the detail of that feel free to grab the code from GitHub and have a look.

The only other style change I made was to add a bit of padding to the labels on the left. I did this by transferring as many of the label settings as I could to a new style called tablabel and then added a padding item to the end. The style in rampstyles.xml ended up looking like this.

	<style name="tablabel">
	    <item name="android:layout_width">wrap_content</item>
	    <item name="android:layout_height">wrap_content</item>
	    <item name="android:height">@dimen/max_setting_label_height</item>
	    <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
	    <item name="android:width">@dimen/max_setting_label_width</item> 
	    <item name="android:paddingLeft">5dp</item>	    
	</style>  

Adding the Splash Screen

Now I’m not a huge fan of pointless splash screens. If they’re keeping the user occupied while something fairly time consuming is going on then fine but in the case of this app, a splash screen was requested regardless. The customer is, however, always right so a splash screen it is.

As I mentioned perviously, an external design agency was commissioned to create an image for the screen and this was imported into the project in a previous step when I grabbed colours out of it to use as the palette for the data entry screens.

I started by creating a new activity called SplashActivity and setting it as the launcher activity. In the Android manifest I removed the intent filter for MainActivity so there was only one. I replaced the default text view with an image view and created a style for it. The activity_splash.xml finished up with the following.

<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"
    tools:context=".SplashActivity" >

    <ImageView
        android:id="@+id/imageView1"
        android:contentDescription="@string/splash_image_content_description"
        style="@style/splashimage" />

</RelativeLayout>

The style splashimage is set in rapstyles.xml as below.

<style name="splashimage">
    <item name="android:src">@drawable/siemenssplashscreen</item>
    <item name="android:layout_width">wrap_content</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:layout_alignParentLeft">true</item>
    <item name="android:layout_alignParentTop">true</item>
    <item name="android:scaleType">fitXY</item>
</style>

The SplashActivity code is also pretty straightforward.

package com.siemens.stem;

import android.os.Bundle;
import android.os.Handler;
import android.view.Window;
import android.view.WindowManager;
import android.app.Activity;
import android.content.Intent;

/**
 * Implements the splash activity.
 * @author Jon Masters
 *
 */
public class SplashActivity extends Activity {

    /*
     * The time the splash screen stays active in ms
     */
    static private int SPLASH_TIME = 3000; 
    
    /**
     * Called when the activity is first created.
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // put the splash screen into full screen.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
            
        setContentView(R.layout.activity_splash);
    }

    /**
     * Called once the activity is displayed
     */
    protected void onResume() {
        super.onResume();
        
        // create a new runnable to handle an event to move to
        // the main activity.
        new Handler().postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                //Finish the splash activity so it can't be returned to.
                SplashActivity.this.finish();
                
                // Create an Intent that will start the main activity.
                Intent mainIntent = new Intent(SplashActivity.this, MainActivity.class);
                SplashActivity.this.startActivity(mainIntent);
            }
        }, SPLASH_TIME);
    }
}

Finishing touches

The app specification calls for the screens to be locked in portrait mode. This is done through the activity configuration xml. I applied the change to both activities because the splash screen would look pretty odd in landscape too.

android:screenOrientation="sensorPortait"

I had to target Android 2.3.3 which is API level 10. In order for the activities to lock into portrait mode either way up depending on the sensor, you have to go with the Google typo (thanks StackOverflow!)

The last thing I did was to create a new icon for the app. I did this by finding an area of the splash screen that looked a bit interesting and then cutting out a 96×96 pixel section. I then scaled down the cut out piece to the other resolutions required i.e. 72×72, 48×48 and 32×32 and imported them into the appropriate drawable folder. I then deleted the old ones and renamed the new ones to replace them.

The final app screens

The final app screens

At this point I decided to call it a day. As is usual in these things, there are a raft of changes I’d like to make to improve the code and the user experience. For one thing, other than the app icon, I’ve not even thought about different resolutions and devices. This might change when I get the updated hardware at which point perhaps I’ll pop up an ‘epilogue’ post. In the meantime though, I’ve uploaded the code so you are welcome to browse through and pull out anything of interest. If you have suggestions for improvements or any (constructive!) feedback I’d be really happy to receive it.

I’ve published all of the source code for this project on GitHub if you’d like to look at it / borrow it. The repo can be found here: https://github.com/jpmasters/stem-ramp-configurator

If you have any questions, comments or suggestions please feel free to let me know.

Advertisements
2 comments
  1. Hello just wanted to give you a brief heads up and let you know a few of the images aren’t loading properly. I’m not sure why but I think its a linking issue.
    I’ve tried it in two different web browsers and both show the same outcome.

    • Thanks for the feedback, much appreciated. I’ll check it out.

      Jon

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: