Tutorial 2 – A Simple Stopwatch (Adding some polish to the design)

In this tutorial we will pick up where we left of from the first tutorial, we had the basic layout completed but we still need to add a bit more polish to get it looking like the final product. We will cover using the styles.xml file for Android, custom fonts and button states. If you did not complete the first tutorial and want to continue from here you can download the xml file and the image resources from the end of the first tutorial. At the end of the first tutorial our app is looking like this, not quite there as yet, but we are making progress.

 

 

Using Custom fonts

For this app I will be mainly using a font named Coolvetica, which can be downloaded from here.

After downloading and unzipping place the font file , that is the .ttf file in the Stopwatch > assets folder. I renamed the .ttf file to coolvetica before placing it in the assets folder, the reason I did this is because it was a lot easier for me to remember, however you do not need to rename yours if you do not wish to.

In order to use the custom font, we need to set the typeface of the various text views via code. For this we need to look into the main activity class.

This is found at Stopwatch > src > com.shawnbe.stopwatchDemo > StopwatchActivity in my example, however it may be different if you used different Project and Activity names. On opening this file you should see the following code. This code is created by default when you setup a new Project.

 

package com.shawnbe.stopwatchDemo;
import android.app.Activity;
import android.os.Bundle;

public class StopwatchActivity extends Activity {
    /** Called when the activity is first created. */

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Before we begin setting the typeface we need to import some packages that we will be using. Since we will be setting the font face for TextViews and Buttons we need to import these, to do this enter the following import statements at the top of your code along with the already existing import statements

import android.app.Activity;
import android.widget.TextView;
import android.graphics.Typeface;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.Button;

Additionally, declare the following variables as we will be using these to temporarily store the TextViews and Buttons before setting the typeface.


    private TextView tempTextView; //Temporary TextView
    private Button tempBtn; //Temporary Button

    @Override
    public void onCreate(Bundle savedInstanceState) {

This goes just above the @Override statement as can be seen above.

Within the onCreate method we will reference the custom text that we placed in the asset folder and then apply the typeface to the stopwatch timer TextView and the text on the three buttons, to do this update the onCreate method so that it contains the following code.


@Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    	/*-------Setting the TextView Fonts-----------*/
    	Typeface font = Typeface.createFromAsset(getAssets(), "coolvetica.ttf");
    	tempTextView = (TextView) findViewById(R.id.timer);
    	tempTextView.setTypeface(font);
    	tempTextView = (TextView) findViewById(R.id.backgroundText);
    	tempTextView.setTypeface(font);
    	Button tempBtn = (Button)findViewById(R.id.startButton);
    	tempBtn.setTypeface(font);
    	tempBtn = (Button)findViewById(R.id.resetButton);
    	tempBtn.setTypeface(font);
    	tempBtn = (Button)findViewById(R.id.stopButton);
    	tempBtn.setTypeface(font);
    }

The first line of the newly added code creates a typeface object based on the font file we placed in the assets folder.

We then find the timer TextView in the second line and in the third line we set the typeface to the typeface object we created in the first line. This is then repeated for the background text and the Button text.

Note: Instead of using the temporary variables we could have done this in one step. Rather than using

tempTextView = (TextView) findViewById(R.id.timer);
tempTextView.setTypeface(font);

we could have used

((TextView) findViewById(R.id.timer)).setTypeface(font);

The latter statement is more efficient but I think the former is easier to understand for this tutorial, feel free to use which ever you are more comfortable with.

If you were to run the project now, you would notice the the font has now been updated to the Coolvetica font.

Using styles (styles.xml)

We can customize the look of a view in Android by using certain properties such as Android:textColor, Android:textSize, Android:layout_height, Android:layout_width as well as many others as show in the example below. Let’s say we wanted to create the timer text with a large text size, dark blue in color, center aligned and with a light blue drop shadow, we can update the timer TextView in the main.xml file to the following.

<TextView
android:textColor="#004254"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:shadowColor="#87CCE4"
android:shadowDx="0"
android:shadowDy="2"
android:shadowRadius="1"
android:textSize="85sp"
android:text="@string/timer"
android:id="@+id/timer">
</TextView>

Now lets say we wanted to have another textView item with the same look, then we would have to repeat most the code in the XML file, this may not be a problem in simper designs, but as your layouts start to get a bit more complex your code can become very cluttered and hard to manage because a change in a certain property would have to be updated at each instance of the textView. Luckily there is way around this. Android allows us to create different styles which can be applied to multiple views, this way you only need to update your code once and the changes will be seen on each instance of the view.

We will create a styles.xml file in the values folder. The folder is located at Stopwatch > res. Right click on the values folder > New > Android XML file. For the file name use “styles” without the quotes, for the type of resource select Values, the folder should be /res/Values and the root element for the XML file should be resources.

Click Finish.

Open the styles.xml file and update the code to the following

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="timerText">
    	<item name="android:layout_width">fill_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textColor">#004254</item>
        <item name="android:gravity">center</item>
        <item name="android:textSize">85sp</item>
        <item name="android:textStyle">bold</item>
        <item name="android:shadowColor">#87CCE4</item>
    	<item name="android:shadowDx">0</item>
    	<item name="android:shadowDy">2</item>
    	<item name="android:shadowRadius">1</item>
    </style>
    <style name="buttonText">
    	<item name="android:layout_width">fill_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_weight">1</item>
        <item name="android:gravity">center</item>
        <item name="android:textSize">45sp</item>
        <item name="android:textStyle">bold</item>
    	<item name="android:shadowDx">0</item>
    	<item name="android:shadowDy">1</item>
    	<item name="android:shadowRadius">1</item>
    </style>
    <style name="backgroundText">
    	<item name="android:layout_width">fill_parent</item>
        <item name="android:layout_height">fill_parent</item>
        <item name="android:gravity">center</item>
        <item name="android:textSize">55sp</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textColor">#77787A</item>
        <item name="android:shadowColor">#BBBDBE</item>
    	<item name="android:shadowDx">0</item>
    	<item name="android:shadowDy">1</item>
    	<item name="android:shadowRadius">1</item>
    </style>
</resources>

The above code specifies all of the properties that we use to format the views to the styles.xml file. There are three different styles that we created, timerText, buttonText and backgroundText. Now we update the different Views and our timer TextView goes from

<TextView
android:textColor="#004254"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:shadowColor="#87CCE4"
android:shadowDx="0"
android:shadowDy="2"
android:shadowRadius="1"
android:textSize="85sp"
android:text="@string/timer"
android:id="@+id/timer">
</TextView>

to a much more manageable

<TextView
style="@style/timerText"
android:text="@string/timer"
android:id="@+id/timer">
</TextView>

and the style can be reused on other views if necessary, we now also update the buttons and the background text.

The background text

<TextView
style="@style/backgroundText"
android:text="@string/backgroundText"
android:id="@+id/backgroundText">
</TextView>

The buttons

<Button
style="@style/buttonText"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="@drawable/stopbutton"
android:textColor="#7A1100"
android:shadowColor="#DF726E"
android:text="@string/stopText"
android:id="@+id/stopButton"
android:visibility="gone">
</Button>
<Button
style="@style/buttonText"
android:layout_marginLeft="5dp"
android:background="@drawable/startbutton"
android:textColor="#5F3A00"
android:shadowColor="#FBEBC5"
android:text="@string/startText"
android:id="@+id/startButton">
</Button>
<Button
style="@style/buttonText"
android:layout_marginRight="5dp"
android:background="@drawable/resetbutton"
android:textColor="#2E2E2E"
android:shadowColor="#959597"
android:text="@string/resetText"
android:id="@+id/resetButton">
</Button>

You may have noticed that some of the formatting properties such as textColor and shadowColor for the buttons are still in the main.xml file rather than the styles.xml file. This is because we need different text and shadow colors for each of the buttons, and we place the common properties such as gravity, layout_width, layout_height, shadowRadius etc in the styles.xml file and the unique properties in the main.xml file. If you run the project now, you will see its a lot closer to the final look.


Button States

In order to enhance the user experience a bit of this app we are going to be adding pressed states for our buttons. Something as simple as a change in button color when pressed greatly enhances the perceived feedback to users. When you tap a button and you see sort of visual feedback it acts as a confirmation that the button was indeed pushed, this is especially helpful if the user has to wait a second or two for some other change on the screen. There are many different button states but we will only be looking at the pressed state in this tutorial.

For more information see the following link on Android State List Drawable.

In order to achieve the different states we are going to create a state list drawable for each button. This is simply an xml file that which specifies various drawable resources to use at different button states. We will be placing these xml files in a drawable folder, which will be created in your res folder.

Right click on your res folder which is located at Stopwatch > res and select New > folder, name this folder drawable.

Right click on the newly created drawable folder and select New > Android XML File.

Name the file startbuttonstates, for type of resource to create select Drawable and at the “Select the root element for the XML file”  drop down list select “Selector”. Click finish.

Repeat this step two more times one named stopbuttonstates and one named resetbuttonstates, these three files will be used for the three buttons.

Open the resetbuttonstates.xml file and update the code to the following

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

In a nutshell this code tells the application when the button is in a pressed state use the resetbuttonpressed drawable resource and when it is in a normal state (unpressed) use the resetbutton drawable resource. Note: you should have the pressed button images from the zip file included in the first tutorial, if you don’t head on over to that Tutorial and download it, unzip the file and place all of the resources in the drawable-hdpi folder. Open the startbuttonpressed.xml and stopbuttonpressed.xml files and update the code to the following respectively. This is for the startbuttonpressed.xml

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

This is for the stopbuttonpressed.xml

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

Save all changes. Now we need to update the android:drawable properties for the buttons in the main.xml file. for the stop button replace the line

android:background="@drawable/stopbutton"

with

android:background="@drawable/stopbuttonstates"

Now instead of the drawable property set to only one image, its checks the stopbuttonstates.xml file and changes the image according to the the button state. Update the android:background properties for the start and reset buttons with the following lines respectively.

android:background="@drawable/startbuttonstates"
android:background="@drawable/resetbuttonstates"

Save your changes and run the project and tap on the buttons, you should now see the change when you press a button. In the image below you can see the start button is in a pressed state.

Using the screen space wisely

If you have a high resolution HDPI screen on your handset you will have more available screen real estate than on a LDPI device and a MDPI device. In this case since there is limited screen space, I dont want to waste the space by having the word stopwatch across the screen under the horizontal line, as well as I don’t want to have the title of the application at the top of the screen. In order to fix the latter, we need to update our Android Manifest file. This is located within the Stopwatch folder. Open the Androidmanifest.xml file so you can view the xml and add the following line

android:theme="@android:style/Theme.NoTitleBar"

after the

android:label="@string/app_name"

line, so that whole section should now look like

<activity android:name=".StopwatchActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar">

This tells the app that you do not want to display the Title bar at the top of the screen. Now to get rid of the stopwatch text if the screen is either LDPI or MDPI. To do this we need to open the Activity file which is located Stopwatch > src > com.shawnbe.stopwatchDemo > StopwatchActivity.java

In this activity we are going to create a new procedure that checks the pixel density of the screen and hides the backgroundtext TextView if it is not and HDPI screen.

In the StopwatchActivity.java file (yours may vary if you named you activity differently) add the following code after the onCreate method which checks the screen density and hides or makes visible the views as necessary.

    private void checkScreenDensity(){
    	tempTextView = (TextView)findViewById(R.id.backgroundText);
    	switch (getResources().getDisplayMetrics().densityDpi) {
    	case DisplayMetrics.DENSITY_LOW:
    		tempTextView.setVisibility(View.GONE);
    	    break;
    	case DisplayMetrics.DENSITY_MEDIUM:
    		tempTextView.setVisibility(View.GONE);
    	    break;
    	case DisplayMetrics.DENSITY_HIGH:
    		tempTextView.setVisibility(View.VISIBLE);
    	    break;
    	}
    }

and also add the following line of code to the onCreate method.

checkScreenDensity();

The onCreate() method now looks like

 @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main2);

        checkScreenDensity();

    	/*-------Setting the TextView Fonts-----------*/

    	Typeface font = Typeface.createFromAsset(getAssets(), "coolvetica.ttf");
    	tempTextView = (TextView) findViewById(R.id.timer);
    	tempTextView.setTypeface(font);
    	tempTextView = (TextView) findViewById(R.id.backgroundText);
    	tempTextView.setTypeface(font);
    	Button tempBtn = (Button)findViewById(R.id.startButton);
    	tempBtn.setTypeface(font);
    	tempBtn = (Button)findViewById(R.id.resetButton);
    	tempBtn.setTypeface(font);
    	tempBtn = (Button)findViewById(R.id.stopButton);
    	tempBtn.setTypeface(font);
    }

That’s the end of this tutorial, in the next tutorial we will be covering the code to get your application working. See you soon

Resources


Android Project up to this point.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>