You should be fairly comfortable with Android Studio by now, so you can expect fewer videos going forward.
Here are the Views in my content_stopwatch.xml
:
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:textSize="92sp" android:text="" android:id="@+id/time_view" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/start" android:id="@+id/start_button" android:layout_below="@+id/time_view" android:layout_centerHorizontal="true" android:layout_marginTop="20dp" android:onClick="onClickStart" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/stop" android:id="@+id/stop_button" android:layout_below="@+id/start_button" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:onClick="onClickStop" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/reset" android:id="@+id/reset_button" android:layout_below="@+id/stop_button" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:onClick="onClickReset" />
Extra code to add to StopWatchActivity.java
(i.e., don't replace the entire file).
Member variables:
// Number of seconds displayed on the stopwatch private int seconds = 0; // Is the stopwatch running? private boolean running;
Methods:
//Start the stopwatch running when the Start button is clicked. public void onClickStart(View view) { running = true; } //Stop the stopwatch running when the Stop button is clicked. public void onClickStop(View view) { running = false; } //Reset the stopwatch when the Reset button is clicked. public void onClickReset(View view) { running = false; seconds = 0; }
Here is some boilerplate repeating Handler
code:
final Handler handler = new Handler(); // Make a new Handler handler.post(new Runnable() { // Give handler a Runnable instance to execute ASAP @Override // Redefine the Runnables's run method public void run() { /* Stuff you * want to do * goes here. */ // Repost the Runnable (this) to run some time in the future. handler.postDelayed(this, MILLISECONDS_UNTIL_handler.post_IS_CALLED_AGAIN); } });
To StopwatchActivity.java
add the following to the end of onCreate()
runTimer();
and the runTimer()
method:
private void runTimer() { final TextView timeView = (TextView)findViewById(R.id.time_view); final Handler handler = new Handler(); handler.post(new Runnable() { @Override public void run() { int hours = seconds/3600; int minutes = (seconds%3600)/60; int secs = seconds%60; String time = String.format("%d:%02d:%02d", hours, minutes, secs); timeView.setText(time); if (running) { seconds++; } handler.postDelayed(this, 1000); } }); }
Be sure you import android.os.Handler
and android.widget.TextView
.
To rotate a stock AVD, use the Ctrl+F11
shortcut to switch to the previous layout orientation and Ctrl+F12
to switch to next layout orientation.
onSaveInstanceState
method to be added to StopwatchActivity
:
@Override public void onSaveInstanceState(Bundle savedInstanceState) { savedInstanceState.putInt("seconds", seconds); savedInstanceState.putBoolean("running", running); }
Code to be added to onCreate()
just before runTimer();
:
if (savedInstanceState != null) { seconds = savedInstanceState.getInt("seconds"); running = savedInstanceState.getBoolean("running"); }
Additional member variable:
private boolean wasRunning;
Restore additional item in onCreate
's if (savedInstanceState != null)
block:
wasRunning = savedInstanceState.getBoolean("wasRunning");
Add additional item to onSaveInstanceState
:
savedInstanceState.putBoolean("wasRunning", wasRunning);
New methods:
@Override protected void onStop() { super.onStop(); wasRunning = running; running = false; }
@Override protected void onStart() { super.onStart(); if (wasRunning) { running = true; } }
You may want to skip these pages until you have completed the app through p. 158.
Note that in the final version of this app, methods onStart
and onStop
are no longer needed. This is because onResume
will be called after onStart
and onPause
will normally be called before onStop
.
Coming up with a test case on an AVD that will partially obscure your app and put it into “Pause” is not trivial. However, if you start and restart the app, it should behave the same as before (even though you have now placed the run/don't run code in onResume
and onPause
.
Don't stress out if you aren't able to test that the changed code works when the app is “Paused” (i.e., not having focus), but do be sure it still works when “Stopped” and “Restarted”. And be sure to understand what Android is doing and the pause/resume process. (P.S., don't forget to read pp. 152-153.)
The diagram is a good one to copy and tape next to your computer.