Is there already a StopWatch class for android and why doesn't my implementation work?
Solution 1
You should use a Chronometer.
But anyway, your code can work if you remove the sleep from UI thread.
private final Runnable mRunnable = new Runnable() {
@Override
public void run() {
if (mStarted) {
long seconds = (System.currentTimeMillis() - t) / 1000;
statusBar.setText(String.format("%02d:%02d", seconds / 60, seconds % 60));
handler.postDelayed(runnable, 1000L);
}
}
};
private Hanlder mHandler;
private boolean mStarted;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
}
@Override
protected void onStart() {
super.onStart();
mStarted = true;
mHandler.postDealyed(runnable, 1000L);
}
@Override
protected void onStop() {
super.onStop();
mStarted = false;
mHandler.removeCallbacks(mRunnable);
}
Solution 2
Have a look at the Chronometer class.
Sample code from APIDemo:
import android.app.Activity;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Chronometer;
public class ChronometerDemo extends Activity {
Chronometer mChronometer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chronometer);
Button button;
mChronometer = (Chronometer) findViewById(R.id.chronometer);
// Watch for button clicks.
button = (Button) findViewById(R.id.start);
button.setOnClickListener(mStartListener);
button = (Button) findViewById(R.id.stop);
button.setOnClickListener(mStopListener);
button = (Button) findViewById(R.id.reset);
button.setOnClickListener(mResetListener);
button = (Button) findViewById(R.id.set_format);
button.setOnClickListener(mSetFormatListener);
button = (Button) findViewById(R.id.clear_format);
button.setOnClickListener(mClearFormatListener);
}
View.OnClickListener mStartListener = new OnClickListener() {
public void onClick(View v) {
mChronometer.start();
}
};
View.OnClickListener mStopListener = new OnClickListener() {
public void onClick(View v) {
mChronometer.stop();
}
};
View.OnClickListener mResetListener = new OnClickListener() {
public void onClick(View v) {
mChronometer.setBase(SystemClock.elapsedRealtime());
}
};
View.OnClickListener mSetFormatListener = new OnClickListener() {
public void onClick(View v) {
mChronometer.setFormat("Formatted time (%s)");
}
};
View.OnClickListener mClearFormatListener = new OnClickListener() {
public void onClick(View v) {
mChronometer.setFormat(null);
}
};
}
R.layout.chronometer:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Chronometer android:id="@+id/chronometer"
android:format="@string/chronometer_initial_format"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="30dip"
android:paddingTop="30dip"
/>
<Button android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chronometer_start">
<requestFocus />
</Button>
<Button android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chronometer_stop">
</Button>
<Button android:id="@+id/reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chronometer_reset">
</Button>
<Button android:id="@+id/set_format"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chronometer_set_format">
</Button>
<Button android:id="@+id/clear_format"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chronometer_clear_format">
</Button>
</LinearLayout>
Add this to Strings.xml
<string name="chronometer_start">Start</string>
<string name="chronometer_stop">Stop</string>
<string name="chronometer_reset">Reset</string>
<string name="chronometer_set_format">Set format string</string>
<string name="chronometer_clear_format">Clear format string</string>
<string name="chronometer_initial_format">Initial format: <xliff:g id="initial-format">%s</xliff:g></string>
Solution 3
You should use a Chronometer.
Chronometer mChronometer;
mChronometer = (Chronometer) findViewById(R.id.chronometer1);
long timeWhenStopped = 0;
when we stop the chronometer
timeWhenStopped = mChronometer.getBase() - SystemClock.elapsedRealtime();
mChronometer.stop();
the chronometer before starting it
mChronometer.setBase(SystemClock.elapsedRealtime() + timeWhenStopped);
mChronometer.start();
resetting the chronometer
mChronometer.setBase(SystemClock.elapsedRealtime());
timeWhenStopped = 0;
Solution 4
Why your code doesn't work is because you post a Runnable
to the UI thread message queue. The runnable runs forever in the while
loop, preventing the UI thread from doing any other business, such as drawing your layout and responding to inputs.
Using Thread.sleep()
in an UI thread is almost always wrong. Consider using a Timer
instead.
principal-ideal-domain
Updated on May 31, 2020Comments
-
principal-ideal-domain almost 4 years
Lately I saw http://developer.android.com/reference/android/os/CountDownTimer.html and wondered if there is an respective class for stop watches since I want to tell the user of my App how long he's already trying to solve the puzzle. I mean it isn't that complicated to program such a stop watch on your own. I tried something like
Runnable runnable = new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { } long seconds = (System.currentTimeMillis() - t) / 1000; statusBar.setText(String.format("%02d:%02d", seconds / 60, seconds % 60)); } } }; statusBar.post(runnable);
But bizarrely the layout of my activity isn't inflated anymore since I have this
statusBar.post(runnable);
in the end of the acitivity'sonCreate
method meaning that after starting the app I only see a white screen instead of the normal gui. -
AZ_ over 9 yearswouldn't it be nicer if you also post a UI screenshot :)
-
Rudolf Real over 9 yearsthe string chronometer_initial_format is missing, could you please add it in this same answer so we avoid going to Google to see the formats?
-
Yaroslav Mytkalyk about 9 years@Ammaraly only OP knows, I modified his code in the first place.
-
Jack about 3 yearsChronometer doesn't support pause and resume, which stopwatches need.