I have a stopwatch app and it has just one activity. Three buttons start, stop, reset and a textview to show the timer. Here is my code :
public class StopwatchActivity extends AppCompatActivity {
private int mNumberOfSeconds = 0;
private boolean mRunning = false;
private Handler handler = new Handler();
private Runnable runnable;
private TextView timeview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stopwatch);
if (savedInstanceState != null){
mNumberOfSeconds = savedInstanceState.getInt("number_of_second");
mRunning = savedInstanceState.getBoolean("is_running");
Log.e("after orient mSecond :" , String.valueOf(mNumberOfSeconds));
Log.e("after orient mRunning :" , String.valueOf(mRunning));
runner();
}
}
public void onClickStart(View view){
handler.removeCallbacks(runnable);
mRunning = true;
runner();
}
public void onClickStop(View view){
mRunning = false;
handler.removeCallbacks(runnable);
}
public void onClickReset(View view){
mRunning = false;
//mNumberOfSeconds = 0;
//timeview.setText("0:00:00");
}
public void runner(){
timeview = (TextView) findViewById(R.id.time_view);
runnable = new Runnable() {
#Override
public void run() {
int hours = mNumberOfSeconds/3600;
int minutes = (mNumberOfSeconds%3600)/60;
int second = mNumberOfSeconds%60;
String time = String.format("%d:%02d:%02d" , hours , minutes , second );
timeview.setText(time);
if (mRunning){
mNumberOfSeconds++;
}
handler.postDelayed(this , 1000);
}
};
handler.post(runnable);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt("number_of_seconds" , mNumberOfSeconds);
outState.putBoolean("is_running" , mRunning);
Log.e("befor rotation second:" , String.valueOf(mNumberOfSeconds));
Log.e("befor rotaion mrunning" , String.valueOf(mRunning));
}
I want to save some of my variables in onSaveInstanceState to get use of them again after orientation changing. As you can see in the code the log message show the value of mNumberOfSeconds and mRunning before and after orientation changes. And after orientation changes mNumberOfSeconds will give me 0 instead of the value I saved. But mRunning gives me the right value. Can anyone give me any solution?
You can avoid this in the future by using tags in the put() and get() calls:
private static final String KEY_NUM_SECS = "NUM_SECS";
then use it like this:
mNumberOfSeconds = savedInstanceState.getInt(KEY_NUM_SECS);
and:
outState.putInt(KEY_NUM_SECS, mNumberOfSeconds);
You can see it clearly if I compare your code:
outState.putInt("number_of_seconds" , mNumberOfSeconds);
mNumberOfSeconds = savedInstanceState.getInt("number_of_second");
you put in value with key "number_of_seconds" but get int value with key "number_of_second", this is the wrong place.
Related
I am trying to show a waiting screen in my app while it's trying to connect my server. For some reason, the screen shows only in the middle of the while loop and sometimes even after the while loop has done its work. Also, even after the while loop gets to its end the code after it doesn't executed (it doesn't print the values). Does anyone know how to fix it?
This is the code:
public class LoadingActivity extends AppCompatActivity {
public static final long SEC_IN_MS = 1000;
public static final int CONNECT_LOOP_TIME = 5; //In seconds
DataSender dataSender = new DataSender();
DataCenter dataCenter = DataCenter.getInstance();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loading);
dataSender.execute();
dataCenter.connect();
}
#Override
protected void onResume() {
super.onResume();
moveToMenu();
}
public void moveToMenu() {
long loopStartTime = System.currentTimeMillis();
boolean didConnect = false;
while(!didConnect &&
System.currentTimeMillis()-loopStartTime <= SEC_IN_MS*CONNECT_LOOP_TIME) {
//Wait until connected or 5 seconds pass
System.out.println(System.currentTimeMillis()-loopStartTime);
if(dataCenter.getRespond().equals(dataCenter.okMsg())) {
didConnect = true;
}
}
System.out.println("111111111111111111");
System.out.println(didConnect);
if(didConnect) {
Intent nextIntent = new Intent(this, MenuActivity.class);
startActivity(nextIntent);
}
else {
System.out.println("OOPS");
}
}
}
first of all excuse me if my title doesn't describe my question very well but i couldn't find a better one .
there is a simple stopWatch app that has three button start,stop,reset and a textview to display time . app has just one activity like this:
public class StopwatchActivity extends AppCompatActivity {
private int mNumberOfSeconds = 0;
private boolean mRunning = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stopwatch);
//if if uncomment this runner method and delete the runner inside onClickStart everything will work find
//runner()
}
public void onClickStart(View view){
mRunning = true;
runner();
}
public void onClickStop(View view){
mRunning = false;
}
public void onClickReset(View view){
mRunning = false;
mNumberOfSeconds = 0;
}
public void runner(){
final TextView timeView = (TextView) findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
int hours = mNumberOfSeconds/3600;
int minutes = (mNumberOfSeconds%3600)/60;
int second = mNumberOfSeconds%60;
String time = String.format("%d:%02d:%02d" , hours , minutes , second );
timeView.setText(time);
if (mRunning){
mNumberOfSeconds++;
}
handler.postDelayed(this , 1000);
}
});
}
}
my problem is when i comment the runner() in onClickStart method and put it in the onCreate method everything is ok . but when i change the code like above the code is still running but after i press stop button and then press start again the second will increment by 4 or 5 very fast.
can anyone explain me what is the difference between this two modes?
declare your handler globally
public void runner(){
timeView = (TextView) findViewById(R.id.time_view);
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
int hours = mNumberOfSeconds/3600;
int minutes = (mNumberOfSeconds%3600)/60;
int second = mNumberOfSeconds%60;
String time = String.format("%d:%02d:%02d" , hours , minutes , second );
timeView.setText(time);
if (mRunning){
mNumberOfSeconds++;
}
handler.postDelayed(this , 1000);
}
}
handler.post(runnable);
}
in button function
public void onClickStart(View view){
if(handler != null) {
//restart the handler to avoid duplicate runnable
handler.removeCallbacks(runnable);//or this handler.removeCallbacksAndMessages(null);
}
mRunning = true;
runner();
}
public void onClickStop(View view){
mRunning = false;
handler.removeCallbacks(runnable); // this will stop the handler from working
}
This question already has answers here:
Update TextView Every Second
(11 answers)
Closed 4 years ago.
i want to update my textview every second.
on button click i am calling one method,
loopMethod(milli); //suppose milli= 50000 i.e 50 sec.
so my loopMethod(int m) is as follows:
public void loopMethod(int m){
timer=(TextView) findViewById(R.id.timerText);
if(m>=1000){
try {
timer.setText(""+m);//timer is a textview
System.out.println(m);
m=m-1000;
Thread.sleep(1000);
} catch(InterruptedException ex) {
ex.printStackTrace();
}
loopMethod(m);
}
}
so what i am expecting is, my timer textview should print the value of m every second.
but i am getting only console output i.e system.out.println(m)...
printing value on console working fine...
but its not updating my textview at all
You can use following code:
Runnable updater;
void updateTime(final String timeString) {
timer=(TextView) findViewById(R.id.timerText);
final Handler timerHandler = new Handler();
updater = new Runnable() {
#Override
public void run() {
timer.setText(timeString);
timerHandler.postDelayed(updater,1000);
}
};
timerHandler.post(updater);
}
In this line:
timerHandler.post(updater);
time will set for the first time. i.e, updater will execute. After first execution it will be posted after every 1 second time interval. It will update your TextView every one second.
You need to remove it when the activity destroys, else it will leak memory.
#Override
protected void onDestroy() {
super.onDestroy();
timerHandler.removeCallbacks(updater);
}
Hope it will help you.
You should use RxJava library to do so:
Subscription s =
Observable.interval(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> {
// update your ui here
}, e -> {
});
// call when you no longer need an update:
if (s != null && !s.isUnsubscribed()){
s.unsubscribe();
s = null;
}
That's it. Do NOT use .postDelay(), Timer because it is error prone.
You might want to consider using the Chronometer class: https://developer.android.com/reference/android/widget/Chronometer.html
just use timer.start(); on the button click
Using handler can be used like this
TextView timer;
int m =0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer=(TextView) findViewById(R.id.timerText);
Handler handler = new UpdateHandler();
m = 10;
handler.sendEmptyMessageDelayed(1, 1000);//start after 1000
}
class UpdateHandler extends Handler{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
timer=(TextView) findViewById(R.id.timerText);
timer.setText("Text :" +m);
m = m-1000;
sendEmptyMessageDelayed(1, 1000); //seng again after 1000
//add some stop logic
break;
default:
break;
}
}
}
Try this code Initialize textview in
onCreate
timer=(TextView) findViewById(R.id.timerText);
public void loopMethod(int m){
if(m>=1000){
try {
System.out.println(m);
m=m-1000;
final ScheduledThreadPoolExecutor c = new ScheduledThreadPoolExecutor(1);
c.schedule(new Runnable() {
#Override
public void run() {
timer.setText(""+m);//timer is a textview
c.shutdownNow();
}
}, 1, TimeUnit.SECONDS);
} catch(InterruptedException ex) {
ex.printStackTrace();
}
loopMethod(m);
}
}
I've added some logics to stop the Timer. If you have any qyestion, ask freely
private int m = 0;
private int milliseconds = 1000;
public void loopMethod(int m){
timer=(TextView) findViewById(R.id.timerText);
Timer t = new Timer();
//schedule a timer
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
timer.setText(String.valueOf(m));//avoid using composite string in the setText
System.out.println(String.valueOf(m));
//remove from the total the amount of millisecond passed
m=m-milliseconds;
if(m <= milliseconds) { //or <= what you want
//stop the timer repeatitions
t.cancel();
}
}
});
}
//"0" is the amount of time to wait for the timer to start
//"milliseconds" is the duration
},0,milliseconds);
}
Add
For a correct analysis you should add more infos in your question. the problem of not-updating textview might be caused by the setText("" + int) because it's always better to avoid the setText with an int. I edited it with String.valueOf, but if it's not working you should add the xml and the onCreate
Hope this helped
I have created timer for seconds.
public class TimerForSeconds extends AppCompatActivity {
private int seconds = 60;
private TextView tvTimer;
private Handler mHandler;
private Runnable runnable = new Runnable() {
#Override
public void run() {
if(seconds == 0){
mHandler.removeCallbacks(runnable);
}
else{
tvTimer.setText(seconds + "");
seconds--;
mHandler.postDelayed(runnable,1000);
}
}
};
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
tvTimer = (TextView) findViewById(R.id.tv_timer);
mHandler = new Handler();
mHandler.postDelayed(runnable,1000);
}
}
//and also removCallback onDestroy too.
I have been running through alot of issues try to pause and unpause a timer, and if I lock the orientation to portrait or landscape it works, but thats not exactly what I want to do. Of course, the onCreate method is called when you change orientation, so im canceling my timertask and setting it to null, but after running through the orientation more than once, it doesnt cancel the timertask anymore. Ive looked through other peoples questions on here but none seem to hold the answer to my quesiton. Heres my code. Its a little sloppy at the moment because ive been trying about everything I can to get it to work.
public class singleTimer extends Activity implements OnClickListener {
private Integer setTime = 0;
private Integer tmrSeconds = 0;
private Integer tmrMilliSeconds = 0;
private Timer myTimer = new Timer();
private TimerTask myTimerTask;
private TextView timerText;
private boolean isPaused = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.single_timer);
Bundle extras = getIntent().getExtras();
setTime = extras.getInt("com.bv.armyprt.timer_duration");
if (myTimerTask != null) {
myTimerTask.cancel();
myTimerTask = null;
}
if (savedInstanceState != null) {
if (savedInstanceState.getInt("tmrSeconds") == 0) {
tmrSeconds = setTime;
} else {
tmrSeconds = savedInstanceState.getInt("tmrSeconds");
tmrMilliSeconds = savedInstanceState.getInt("tmrMilliseconds");
if (isPaused == false) {
myTimer = new Timer();
myTimerTask = new TimerTask() {
#Override
public void run() {
TimerMethod();
}
};
myTimer.schedule(myTimerTask, 0, 100);
}
}
} else {
tmrSeconds = setTime;
}
timerText = (TextView)findViewById(R.id.timerText);
timerText.setText(String.format("%03d.%d", tmrSeconds, tmrMilliSeconds));
TextView timerDesc = (TextView)findViewById(R.id.timerDescription);
timerDesc.setText("Timer for: " + setTime.toString());
Button startButton = (Button)findViewById(R.id.timerStart);
Button stopButton = (Button)findViewById(R.id.timerStop);
Button closeButton = (Button)findViewById(R.id.timerClose);
closeButton.setOnClickListener(this);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case (R.id.timerStart):
isPaused = false;
myTimer = new Timer();
myTimerTask = new TimerTask() {
#Override
public void run() {
TimerMethod();
}
};
myTimer.schedule(myTimerTask,0, 100);
break;
case (R.id.timerStop):
isPaused = true;
myTimerTask.cancel();
myTimerTask = null;
myTimer.cancel();
break;
case (R.id.timerClose):
onDestroy();
this.finish();
break;
}
}
private void TimerMethod()
{
//This method is called directly by the timer
//and runs in the same thread as the timer.
//We call the method that will work with the UI
//through the runOnUiThread method.
this.
tmrMilliSeconds--;
this.runOnUiThread(Timer_Tick);
}
private Runnable Timer_Tick = new Runnable() {
public void run() {
//This method runs in the same thread as the UI.
if (tmrSeconds > 0) {
if (tmrMilliSeconds <= 0) {
tmrSeconds--;
tmrMilliSeconds = 9;
}
} else {
Vibrator v = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(1000);
myTimer.cancel();
tmrSeconds = setTime;
tmrMilliSeconds = 0;
isPaused = true;
}
//Do something to the UI thread here
timerText.setText(String.format("%03d.%d", tmrSeconds, tmrMilliSeconds));
}
};
#Override
public void onSaveInstanceState(Bundle savedInstanceState){
savedInstanceState.putInt("setTimer", setTime);
savedInstanceState.putInt("tmrSeconds", tmrSeconds);
savedInstanceState.putInt("tmrMilliseconds", tmrMilliSeconds);
super.onSaveInstanceState(savedInstanceState);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
setTime = savedInstanceState.getInt("setTimer");
tmrSeconds = savedInstanceState.getInt("tmrSeconds");
tmrMilliSeconds = savedInstanceState.getInt("tmrMilliSeconds");
}
}
you can simply add a boolean variable
boolean stopTImer = false ;
and in your timerTask , do something like this :
#Overrride
public void run(){
if(!stopTimer){
//do stuff ...
//...
}
and when you want to stop it , put the boolean to true
You should stop the timer during onStop. Android might create another instance of your Activity and you will lose the reference to your previous timer(task) when you change orientation.
All objects tied to an activity follow the activity lifecycle. That means you have to store the references to objects elsewhere if you want to keep them even if the activity gets deleted (which can happen quite often).
Im working on a small application to try out an idea that I have. The idea is to periodically update the UI when event of some sort occurs. In the demo I've created, I'm updating a ProgressDialog every 2 seconds for 15 turns.
The problem I am having, which I don't quite understand is that when an event is handled, I send a message to the handler which is supposed to update the message in the ProgressDialog. When this happens however, I get an exception which states that I can't update the UI from that thread.
The following code appears in my Activity:
ProgressDialog diag;
String diagMessage = "Started loading...";
final static int MESSAGE_DATA_RECEIVED = 0;
final static int MESSAGE_RECEIVE_COMPLETED = 1;
final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg){
diag.setMessage(diagMessage);
switch(msg.what){
case MESSAGE_DATA_RECEIVED:
break;
case MESSAGE_RECEIVE_COMPLETED:
dismissDialog();
killDialog();
break;
}
}
};
Boolean isRunning = false;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupDialog();
if(isRunning){
showDialog();
}
setContentView(R.layout.main);
}
void setupDialog(){
if(diag == null){
diag = new ProgressDialog(ThreadLoading.this);
diag.setMessage(diagMessage);
}
}
void showDialog(){
isRunning = true;
if(diag != null && !diag.isShowing()){
diag.show();
}
}
void dismissDialog(){
if(diag != null && diag.isShowing()){
diag.dismiss();
}
}
void killDialog(){
isRunning = false;
}
public void onStart(){
super.onStart();
showDialog();
Thread background = new Thread(new Runnable(){
public void run(){
try{
final ThreadRunner tr = new ThreadRunner();
tr.setOnDataReceivedListener(new ThreadRunner.OnDataReceivedListener(){
public void onDataReceived(String message){
diagMessage = message;
handler.handleMessage(handler.obtainMessage(MESSAGE_DATA_RECEIVED));
}
});
tr.setOnDataDownloadCompletedEventListener(new ThreadRunner.OnDataDownloadCompletedListener(){
public void onDataDownloadCompleted(String message){
diagMessage = message;
handler.handleMessage(handler.obtainMessage(MESSAGE_RECEIVE_COMPLETED));
}
});
tr.runProcess();
}
catch(Throwable t){
throw new RuntimeException(t);
}
}
});
background.start();
}
#Override
public void onPause(){
super.onPause();
dismissDialog();
}
For curiosity sake, here's the code for the ThreadRunner class:
public interface OnDataReceivedListener {
public void onDataReceived(String message);
}
public interface OnDataDownloadCompletedListener {
public void onDataDownloadCompleted(String message);
}
private OnDataReceivedListener onDataReceivedEventListener;
private OnDataDownloadCompletedListener onDataDownloadCompletedEventListener;
int maxLoop = 15;
int loopCount = 0;
int sleepTime = 2000;
public void setOnDataReceivedListener(OnDataReceivedListener onDataReceivedListener){
this.onDataReceivedEventListener = onDataReceivedListener;
}
public void setOnDataDownloadCompletedEventListener(OnDataDownloadCompletedListener onDataDownloadCompletedListener){
this.onDataDownloadCompletedEventListener = onDataDownloadCompletedListener;
}
public void runProcess(){
for(loopCount = 0; loopCount < maxLoop; loopCount++){
try{
Thread.sleep(sleepTime);
onDataReceivedEventListener.onDataReceived(Integer.toString(loopCount));
}
catch(Throwable t){
throw new RuntimeException(t);
}
}
onDataDownloadCompletedEventListener.onDataDownloadCompleted("Download is completed");
}
Am I missing something? The logic makes sense to me and it looks like everything should work, I'm using a handler to update the UI like it is recommended.
Any help will be appreciated.
Thanks,
Tyrone
P.S. I'm developing for Android 1.5
I found the problem. After comparing my code with someone else's code which was very similar, the following small problem was found:
handler.handleMessage(handler.obtainMessage(MESSAGE_RECEIVE_COMPLETED));
Should actually be:
handler.sendMessage(handler.obtainMessage(MESSAGE_RECEIVE_COMPLETED));
Hopefully someone finds this useful and learns from my mistake :)
Regards,
Tyrone