Tutorial 3 – A Simple Stopwatch (Lets Add the code)

Update: As with all projects, you may reach to a point only to realize that there is a problem with your original concept, something that you missed and only recognized while in the middle of the project. When issues like this arise we have no choice but to make a suitable change to fix the issue and move continue on. While working on this part of the project I came across two problems.

  1. The font I chose for the timer text. I originally choose Coolvetica because it liked the look of it in the design mock up, however when running it during the development of the app, I noticed because of the different widths of each of the characters as the numbers on the timer change they would either shift a bit to the left or right depending on which numbers are displayed, this makes it a bit hard to read the numbers. This is just my opinion and you are free to use which ever font you like and your stopwatch would still work, this is just a matter of personal preference for me. Because of this there has been a slight design change from the original.
  2. In my original plan for the stopwatch I wanted to keep it as simple as possible and make the stopwatch accurate to the second, but I realized that most people would want a stopwatch that was at least accurate to 1/10th of a second.
In order to facilitate the change, I had to make a couple of changes.
  • Firstly I added an extra TextView to the timerBackground Linear layout that will display the 1/10th of a second. Because the text in that TextView would change frequently, I decided to make it a bit smaller and thus less distracting. The following code was added in the timerBackground LinearLayout after the timer TextView, and the font size of the timer TextView has been changed from 85sp to 70sp
	<TextView
	style="@style/timerText"
	android:text="@string/timerMs"
	android:id="@+id/timerMs"
	android:textSize="30sp"
	android:paddingTop="30sp">
	</TextView>
  • The second update I made was to use an extra custom font for the timer TextView and the newly added timerMs TextView. The new font Alte Haas Grotesk can be downloaded here. Note: When placing the font in the assets file the name should be in all lowercase if not it would result in an error when running the application.
Now back to the tutorial.

Picking up where we left off

Up to this point we have created the layout for the stopwatch, from here on we will be adding some code to make the stopwatch function. If you would like to follow along from this point, the files are available at the end of Tutorial 2.

The main purpose of this stopwatch is to time events by checking the amount of time that has elapsed from the start of the event to the end. In order to calculate the elapsed time we are going to minus the end time from the start time. When the start button is pressed we are going to record that time as the start time and at regular intervals we will be checking the current time which will be subtracted from the start time, the timer TextView will be updated with the calculated elapsed time.

At this point I am going to assume you know where the relevant files are located within your project and no longer write out the path for each one. Just for a refresher the two main files that we will be using today are the main.xml file and the StopwatchActivity.java file. The locations within the project folder are as follows

Stopwatch > src > com.shawnbe.stopwatchDemo > StopwatchActivity.java if you are following along with my files

and for the main.xml file

Stopwatch > res > layout > main.xml if you are using my files.

 

Handling the Button clicks

Before we continue, lets wire up the button click procedures, these are the procedures that will be called when a specific button is pressed. This is a relatively painless process in Android. Open the main.xml file and find the buttonarea LinearLayout that contains all three of our buttons. For each of the button we are going to set the onClick property, to do this add the following lines to the stopButton in the main.xml file

android:onClick="stopClick"

Similarly for the start and reset buttons add the following lines.

android:onClick="startClick"
android:onClick="resetClick"

The app now knows when the start button is clicked to call the startClick procedure, when the stop button is clicked call the stopButton procedure and the same for the reset button. Lets go ahead and create the procedures. (for now we will be creating empty procedures, but we will be adding code to them as we go along)

Open the StopwatchActivity.java and add the following lines of code.

public void startClick (View view){

}

public void stopClick (View view){

}

public void resetClick (View view){

}

These procedures are empty and currently do nothing, but we have just set up the click handlers for the three different buttons and we will be using them shortly.

 

Hiding / Showing the correct buttons

If you remember from the first tutorial, we added three buttons to the main.xml file, but only two of them are visible on the display, this is because there is no need to show the stop button when it is not needed as it would result in an unnecessarily cluttered interface. The stop button is only needed after the stopwatch has been started, using at any other time is useless as it would result in no change, this is also similar for the start button, we do not need the start button to be visible while the stopwatch is already running.

When the start button is pressed, it as well as the reset button will be replaced by one large stop button and when the stop button is pressed it will be replaced by the start and reset buttons. If the stopwatch had a lap timer function it would be a good idea to replace the reset button with a lap button, but as we are building a simple stopwatch at this time and we will not be adding a lap function, this can be a good exercise for you to try on your own.

In the StopwatchActivity.java file we will be adding two new procedures, one to hide the start and reset buttons and display the stop button and another procedure to do the reverse, hide the stop button and show the start and reset buttons. Add the two following procedures to the StopwatchActivity.java file.

private void showStopButton(){
    ((Button)findViewById(R.id.startButton)).setVisibility(View.GONE);
    ((Button)findViewById(R.id.resetButton)).setVisibility(View.GONE);
    ((Button)findViewById(R.id.stopButton)).setVisibility(View.VISIBLE);
}

private void hideStopButton(){
    ((Button)findViewById(R.id.startButton)).setVisibility(View.VISIBLE);
    ((Button)findViewById(R.id.resetButton)).setVisibility(View.VISIBLE);
    ((Button)findViewById(R.id.stopButton)).setVisibility(View.GONE);
}

 

When the showStopButton() procedure is called, it hides the start and reset buttons by setting the visibility to VIEW.GONE and shows the stop button by setting the visibility to VIEW.VISIBLE, and vice versa for the hideStopButton() procedure. The procedures are created but they are not being called or used anywhere, we therefore now need to call these procedures when the start and stop buttons are clicked, to do this update the onClick procedures we created earlier to include the following code

public void startClick (View view){
   showStopButton();
}

public void stopClick (View view){
   hideStopButton();
}

If you wish you can run the application now to see the button change when pressed.

Convert the milliseconds to a String.

A little earlier in this tutorial we mentioned the basic idea that we will be using to calculate the elapsed time, I did not mention however that the calculations will return the elapsed time in milliseconds. In order to provide a easily readable format that people are accustomed we need to write another procedure. While we could show the results in milliseconds, it would be a lot more readable and easy to comprehend if we display it in it milliseconds, seconds, minutes and hours.

For example if 150000 milliseconds has elapsed, this might be easy for some to understand, but if we display it as 2 minutes and 30 seconds then this the easiest for the majority of the people to read and interpret as they are familiar with this format. Our next step is to create a procedure that accepts milliseconds as an argument in a long format and then displays the result in terms of hours, minutes, seconds in the timer TextView and the milliseconds accurate to 1/10 of a second in the timerMs TextView.  Open your StopwatchActivity.java file or your corresponding activity.java file and add the following procedure to your code.


private void updateTimer (float time){
		secs = (long)(time/1000);
		mins = (long)((time/1000)/60);
		hrs = (long)(((time/1000)/60)/60);

		/* Convert the seconds to String
		 * and format to ensure it has
		 * a leading zero when required
		 */
		secs = secs % 60;
		seconds=String.valueOf(secs);
    	if(secs == 0){
    		seconds = "00";
    	}
    	if(secs <10 && secs > 0){
    		seconds = "0"+seconds;
    	}

		/* Convert the minutes to String and format the String */

    	mins = mins % 60;
		minutes=String.valueOf(mins);
    	if(mins == 0){
    		minutes = "00";
    	}
    	if(mins <10 && mins > 0){
    		minutes = "0"+minutes;
    	}

    	/* Convert the hours to String and format the String */

    	hours=String.valueOf(hrs);
    	if(hrs == 0){
    		hours = "00";
    	}
    	if(hrs <10 && hrs > 0){
    		hours = "0"+hours;
    	}

    	/* Although we are not using milliseconds on the timer in this example
    	 * I included the code in the event that you wanted to include it on your own
    	 */
    	milliseconds = String.valueOf((long)time);
    	if(milliseconds.length()==2){
    		milliseconds = "0"+milliseconds;
    	}
      	if(milliseconds.length()<=1){
    		milliseconds = "00";
    	}
		milliseconds = milliseconds.substring(milliseconds.length()-3, milliseconds.length()-2);

		/* Setting the timer text to the elapsed time */
		((TextView)findViewById(R.id.timer)).setText(hours + ":" + minutes + ":" + seconds);
		((TextView)findViewById(R.id.timerMs)).setText("." + milliseconds);
	}

In a nutshell the procedure takes the time in milliseconds and splits in into hours, minutes, seconds and even milliseconds. Since we want the time in a consistent format like 00:00:00 we check the hours, minutes and seconds to see if they are less than 10, if they are we add an extra zero to the front of the number, so it would display as 05 instead of just 5.

The modulo function (%) is used to remove any remainders of the hour, minute or second. If we have 2.5 minutes elapsed we want the minute portion of the string to be 2 not 2.5, the .5 will be converted to 30 seconds and represented in the seconds portion of the string. At the end of the procedure the timer and timerMs TextViews are updated to display the elapsed time.

Declaring variables

Before we start coding the calculation of the elapsed time lets declares some variables that we will be using through out the class.

Before the @Override line of the onCreate method add the following declarations. Some may have already been declared in earlier tutorials, so no need to repeat those.

private TextView tempTextView; //Temporary TextView
private Button tempBtn; //Temporary Button
private Handler mHandler = new Handler();
private long startTime;
private long elapsedTime;
private final int REFRESH_RATE = 100;
private String hours,minutes,seconds,milliseconds;
private long secs,mins,hrs,msecs;
private boolean stopped = false;

The startTime long variable will be used to keep track of the time the start button is pressed, the stopTime will keep track of the time the stop button is pressed. Why are these times important? If I start the timer at 1:05PM  and I recheck the time at 1:07 PM, I can subtract the two and figure out that two minutes has elapsed since I started the stopwatch.

Now if in the middle of timing some event I am disturbed and I decide to pause the timing and continue with it later, if I record the time that has already elapsed when I am ready I can continue from that position rather than having to restart the whole process.

The REFRESH_RATE defines how often we should update the timer to show how much time has elapsed. This value is in milliseconds. If it is set to 100, the every tenth of a second we will update the timer on the stopwatch, if it was set to 1000 then we would update the timer every second.

The next two lines of declarations are variables that will be used in the procedure defined above that converts the milliseconds to an easily readable format.

The last variable boolean, keeps track of whether the stopwatch has been stopped or not, this will let us know if the stopwatch has been paused during a timing and if it should continue where it left off or if it should start a new timer from zero.

 

Creating the timer Runnable

When we click on the start button we want to execute a piece of runnable code to start the timer, this is where the calculation that we discussed earlier takes place. To create the runnable open the StopwatchActivity.java file and add the following code.

private Runnable startTimer = new Runnable() {
   public void run() {
	   elapsedTime = System.currentTimeMillis() - startTime;
	   updateTimer(elapsedTime);
	   mHandler.postDelayed(this,REFRESH_RATE);
	}
};

This code calculates the elapsed time by subtracting the current time (System.currentTimeMillis()) from the start time, then updates the timer TextView with the elapsed time so it can be seen visually on your handset then waits for 1/10 second (which is what the REFRESH_RATE is set to) and the checks the elapsed time again. This will continue until the stop button is pressed or until the activity is destroyed.

We created the runnable above but we did not call it from the startClick procedure. Lets do that now as well as reset the timer when the reset button is pressed as well as update the stopbuttonclick procedure to stop the timer from updating when the button is pressed. In your StopwatchActivity.java file update the stopClick, startClick and resetClick procedures to match the following.


   public void startClick (View view){
    	showStopButton();
    	if(stopped){
    		startTime = System.currentTimeMillis() - elapsedTime;
    	}
    	else{
    		startTime = System.currentTimeMillis();
    	}
    	mHandler.removeCallbacks(startTimer);
        mHandler.postDelayed(startTimer, 0);
    }

    public void stopClick (View view){
    	hideStopButton();
    	mHandler.removeCallbacks(startTimer);
    	stopped = true;
    }

    public void resetClick (View view){
    	stopped = false;
    	((TextView)findViewById(R.id.timer)).setText("00:00:00");
    	((TextView)findViewById(R.id.timerMs)).setText(".0");
    }

Lets start by looking at the stopClick procedure. When the stop button is pressed, the handler stops the app from looping in the runnable event, and it sets the stopped boolean value to true, this allows us to know that the stopwatch was stopped.

When we now click on the start button, the code tells the stopwatch to check if the stopwatch was stopped if it was then the stopwatch should resume from where it left off, this is done by adding the already elapsed time to the new calculations of the elapsed time. If it was not stopped then get the current time and this will be our start time. The app then checks to ensure that there are no instances of the startTimer runnable currently running and then starts a new thread to update the timer every one tenth of a second.

Once you have this code saved in your StopwatchActivity.java file, go ahead and run the project and you should have a working stopwatch.

 

That’s it for this tutorial.

 

Overview of next Tutorial

For the next tutorial we will be covering how to handle switching between landscape and portrait modes and adding ads to your app.

 

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>