I am trying to create a simple Android stopwatch application. I was having trouble with the application freezing every time I would hit the start button. I learned from reading various things online that the reason it hangs is that I ran a while loop in the UI thread and in order for the application not to crash, that while loop had to be somewhere different. A post on the XDA forums suggested that someone encountering this problem should use an AsyncTask to accomplish this. I am having trouble understanding exactly how to use AsyncTask to do this.
TL;DR: I am trying to count time and then have it update a textview with the corresponding time
Original code with while loop in UI thread
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity
{
Button start, stop, reset;
TextView time;
boolean timeStopped = false;
long timeInNanoSeconds, startTimeInNanoSeconds;
double timer;
public double getTimeInSeconds()
{
timeInNanoSeconds = System.nanoTime() - startTimeInNanoSeconds;
double timeSeconds = (double) timeInNanoSeconds / 1000000000.0;
double roundOff = Math.round(timeSeconds * 100.0) / 100.0;
return roundOff;
}
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.startButton);
stop = (Button) findViewById(R.id.stopButton);
reset = (Button) findViewById(R.id.resetButton);
time = (TextView) findViewById(R.id.timeField);
start.setOnClickListener(new View.OnClickListener()
{
public void onClick(View arg0)
{
startTimeInNanoSeconds = System.nanoTime();
while(timeStopped == false)
{
double timer = getTimeInSeconds();
String stringTimer = Double.toString(timer);
CharSequence sequenceTimer = stringTimer;
time.setText(sequenceTimer);
}
}
});
stop.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
}
});
reset.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
time.setText("");
}
});
}
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
EDIT: Working version using Handler
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
Button start, stop, reset;
TextView time;
Handler m_handler;
Runnable m_handlerTask;
int timeleft = 0;
boolean timeStopped;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.buttonStart);
stop = (Button) findViewById(R.id.buttonStop);
reset = (Button) findViewById(R.id.buttonReset);
time = (TextView) findViewById(R.id.textTime);
start.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
timeStopped = false;
m_handler = new Handler();
m_handlerTask = new Runnable()
{
public void run() {
if(timeStopped == false){
if(timeleft > -1) {
Log.i("timeleft","" + timeleft);
time.setText(String.valueOf(timeleft));
timeleft++;
}
else{
m_handler.removeCallbacks(m_handlerTask);
}
}
m_handler.postDelayed(m_handlerTask, 1000);
}
};
m_handlerTask.run();
}
});
stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timeStopped = true;
m_handler.removeCallbacks(m_handlerTask);
}
});
reset.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timeStopped = true;
m_handler.removeCallbacks(m_handlerTask);
timeleft = 0;
time.setText(String.valueOf(timeleft));
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
doInbackground is invoked on the background thread. you cannot update ui from a background
time.setText(sequenceTimer);
// should be in a ui thread.
Use runOnUithread or setText in onPostExecute.
You can use a Handler , a timer task or a CountDowntimer depending on your requirement.
Android Thread for a timer
Edit:
Using Handler
public class MainActivity extends Activity
{
Button start;
TextView time;
Handler m_handler;
Runnable m_handlerTask ;
int timeleft=100;
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.button1);
time = (TextView)findViewById(R.id.textView1);
start.setOnClickListener(new View.OnClickListener()
{
public void onClick(View arg0)
{
m_handler = new Handler();
m_handlerTask = new Runnable()
{
#Override
public void run() {
if(timeleft>=0)
{
// do stuff
Log.i("timeleft",""+timeleft);
time.setText(String.valueOf(timeleft));
timeleft--;
}
else
{
m_handler.removeCallbacks(m_handlerTask); // cancel run
}
m_handler.postDelayed(m_handlerTask, 1000);
}
};
m_handlerTask.run();
}
});
}
}
In my opinion AsyncTask is not fit for you, as this in my mind is a single shot action.
I would suggest something like this:
private ScheduledExecutorService exec;
private void startExec() {
shutDownExec();
exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleWithFixedDelay(new Runnable() {
#Override
public void run() {
// this starts immediately and is run once every minute
double timer = getTimeInSeconds();
String stringTimer = Double.toString(timer);
CharSequence sequenceTimer = stringTimer;
runOnUiThread(new UpdateUI(R.id.yourtime_textview, sequenceTimer));
}
}, 0, 1, TimeUnit.MINUTES); // adjust how often run() is executed here
}
private void shutDownExec() {
if (exec != null && !exec.isTerminated()) {
exec.shutdown();
}
}
private class UpdateUI implements Runnable {
private String mText;
private TextView mTv;
public UpdateUI(int textviewid, String text) {
this.mTv = (TextView) findViewById(textviewid);
this.mText = text;
}
#Override
public void run() {
mTv.setText(mText);
}
}
I had to do a similar task lately I used created a separate thread with the following code. It lets you update at set time intervals which I think would be suited to your task.
Hope it helps.
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
public class AutomationTreadClass {
Activity refToUIActivity;
//Declare the timer
Timer t = new Timer();
//pass UI activity so you can call update on it
AutomationTreadClass( Activity callingActivity ){
refToUIActivity = callingActivity;
startTimerTread();
}
private void startTimerTread(){
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
//do any updates to the time you need to do here
updateLevelMeter();
}
},
//Start Time of thread
0,
//interval of updates
30);
}
private void updateLevelMeter() {
refToUIActivity.runOnUiThread(new Runnable() {
public void run() {
//access what ever UI comment you need to here. like giving you textview a value.
}
});
}
}
Related
first of all pardon my English, but I think it's understandable.
So in this app is a counter and I need it to do sound when it reaches for example 30 seconds.
note: this is my first question here so if I broke any rules or I could have asked better way, let me know please
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Chronometer;
public class MainActivity extends AppCompatActivity {
private Button mStartButton;
private Button mPauseButton;
private Button mResetButton;
private Chronometer mChronometer;
private long lastPause;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mStartButton = (Button) findViewById(R.id.start_button);
mPauseButton = (Button) findViewById(R.id.pause_button);
mResetButton = (Button) findViewById(R.id.reset_button);
mChronometer = (Chronometer) findViewById(R.id.chronometer);
mStartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (lastPause != 0){
mChronometer.setBase(mChronometer.getBase() + SystemClock.elapsedRealtime() - lastPause);
}
else{
mChronometer.setBase(SystemClock.elapsedRealtime());
}
mChronometer.start();
mStartButton.setEnabled(false);
mPauseButton.setEnabled(true);
}
});
mPauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
lastPause = SystemClock.elapsedRealtime();
mChronometer.stop();
mPauseButton.setEnabled(false);
mStartButton.setEnabled(true);
}
});
mResetButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mChronometer.stop();
mChronometer.setBase(SystemClock.elapsedRealtime());
lastPause = 0;
mStartButton.setEnabled(true);
mPauseButton.setEnabled(false);
}
});
}
}
Define global integer variable as counter and count it in Chronometer's OnChronometerTickListener, and when it reach for example 30 then play sound and reset your counter:
int c = -1; // define global
chronometer.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer chronometer) {
c++;
if(c == 30) {
c = 0;
MediaPlayer mp = MediaPlayer.create(getApplicationContext(), R.raw.ding);
mp.start();
}
}
});
I have designed an app that displays text when tapped once and displays it differently on a Long Press and on a Double Tap. However, I observe that the methods are called once. That is once I either long press or tap once or double tap, the corresponding method is called and then on subsequent tapping or press does not do anything. What can be done to make the app work not just once?
package com.example.hello;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.OnGestureListener;
import android.view.ViewGroup.LayoutParams;
import android.view.View.OnTouchListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnClickListener;
public class MainActivity extends ActionBarActivity implements OnTouchListener {
private TextView shownamecenter;
private TextView shownamecustom;
private RelativeLayout myimage;
int x, y;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
shownamecenter = (TextView)findViewById(R.id.shownamecenter);
shownamecustom = (TextView)findViewById(R.id.shownamecustom);
myimage = (RelativeLayout)findViewById(R.id.myimage);
shownamecenter.setText("");
shownamecustom.setText("");
myimage.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
showNameOnSingleTap();
return;
}
});;
myimage.setOnTouchListener(this); //{
myimage.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
showNameInCustomPosition(shownamecustom, x, y);// TODO Auto-generated method stub
return true;
}
});
return;
}
private void showNameOnSingleTap() {
Timer countdown = new Timer(false);
countdown.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
shownamecenter.setVisibility(View.INVISIBLE);
}
});
}
},3000);
shownamecustom.setText("");
shownamecenter.setText("My Text");
shownamecenter.setTextColor(0xff00ff00);
RelativeLayout.LayoutParams layoutparameters = (RelativeLayout.LayoutParams)shownamecenter.getLayoutParams();
layoutparameters.addRule(RelativeLayout.CENTER_IN_PARENT, -1);
shownamecenter.setLayoutParams(layoutparameters);
findViewById(R.id.myimage).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
shownamecenter.setText("");
return;
}
});
return;
}
private void showNameInCustomPosition(TextView customview, int x, int y) {
Timer countdown = new Timer(false);
countdown.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
shownamecustom.setVisibility(View.INVISIBLE);
}
});
}
},3000);
RelativeLayout.LayoutParams relout = (RelativeLayout.LayoutParams) customview.getLayoutParams();
relout.leftMargin = x-50;
relout.topMargin = y-50;
customview.setLayoutParams(relout);
shownamecenter.setText("");
shownamecustom.setText("My Text");
shownamecustom.setTextColor(0xff00ff00);
class GestureListener extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onDoubleTap(MotionEvent e) {
shownamecustom.setText("");
showNameOnSingleTap();
return true;
}
}
return;
}
//}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
x = (int)event.getRawX();
y = (int)event.getRawY();
return false;
}
}
The problem is because you are redefining what happens when you click on the image, which isn't terribly good practice because it makes it difficult to keep track of what should be going on. A View can only ever have one Listener of each type, so when you do
private void showNameOnSingleTap() {
//...
findViewById(R.id.myimage).setOnClickListener (new View.OnClickListener() {
#Override public void onClick(View v) {
shownamecenter.setText(""); return;
}
});
}
You are changing what happens every time that you click the image after that. Rather than calling your method showNameOnSingleTap() when it's clicked, it is only setting the text and not calling your method. Instead of reassigning the onClickListener, just keep a boolean variable to determine what you want to do.
boolean textIsVisible = false;
private void showNameOnSingleTap() {
if(textIsVisible) {
//hide text
textIsVisible = false;
shownamecenter.setText(View.INVISIBLE);
}
else {
Timer countdown = new Timer(false);
countdown.schedule(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
public void run() {
shownamecenter.setVisibility(View.INVISIBLE);
textIsVisible = false;
}
});
}
},3000);
//show text
textIsVisible = true;
}
}
Good afternoon,
I am looking to assign a generic variable that can be changed throughout the app and passed into CountDownTimer as my long
package com.rcd.learningactivities;
import java.text.DecimalFormat;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
protected CountDownTimer cd;
private long countTime = 0; // Variable for CountDownTimer
private Button lastPressedButton;
private Button red;
private Button blue;
private TextView blueTimer;
private TextView redTimer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
blueTimer = (TextView) findViewById(R.id.blueTimer);
blue = (Button) findViewById(R.id.button1);
redTimer = (TextView) findViewById(R.id.redTimer);
red = (Button) findViewById(R.id.button2);
cd = new CountDownTimer(countTime, 1000) { //<--- trying to use countTime here
public void onTick(long millisUntilFinished) {
DecimalFormat dfmin = new DecimalFormat("0");
DecimalFormat dfsec = new DecimalFormat("00");
double seconds = TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished)%60;
double min = TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished);
if (lastPressedButton == blue){
blueTimer.setText(String.valueOf(dfmin.format(min)) + ":" + String.valueOf(dfsec.format(seconds)));
}
else if (lastPressedButton == red) {
redTimer.setText(String.valueOf(dfmin.format(min)) + ":" + String.valueOf(dfsec.format(seconds)));
}
}
public void onFinish() {
if (lastPressedButton == blue){
blueTimer.setText("5:00");
}
else if (lastPressedButton == red){
redTimer.setText("5:00");
}
}
};
blue.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
lastPressedButton = blue;
countTime = 300000; // <-- setting countTime here
if (blueTimer.getText().toString().contains("5:00")){
cd.start();
} else {
cd.cancel();
cd.onFinish();
}
}
});
red.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
lastPressedButton = red;
countTime = 250000;
if (redTimer.getText().toString().contains("5:00")){
cd.start();
} else {
cd.cancel();
cd.onFinish();
}
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
For whatever reason, it stays with the initially declared number 0 (or if i change it to say 300000 then its always 300000) and its not changing the countTime below in my onClick.
Any help is greatly appreciated. If it can be accompanied by an explanation, that would be great!
Thanks everyone in advance!
EDIT: Im guessing its a scope issue that im overlooking??
EDIT2: If it is a scope issue, im a little confused as to how im able to reset the "lastPressedButton" variable set just about it, but not the countTime.
Java has pass by value semantics, not pass by reference, the value of countTime at construction is passed to CountDownTimer, CDT does not see any updates to countTime after it has been instantiated.
package com.example.progressdialog;
import com.example.progressdialog.R;
import android.os.Bundle;
import android.app.Activity;
import android.app.ProgressDialog;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
private ProgressDialog progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progress = new ProgressDialog(this);
}
public void open(View view){
progress.setMessage("Start This Baby Up!");
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progress.setIndeterminate(false);
progress.show();
final int totalProgressTime = 100;
final Thread t = new Thread(){
#Override
public void run(){
int jumpTime = 0;
while(jumpTime < totalProgressTime){
try {
sleep(500);
jumpTime += 1;
progress.setProgress(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
I cannot seem to get my progress bar to actually progress forward. It simply displays a static 0/100 bar. I am trying to get it to move forward in a smooth manner taking approximately 30-45 seconds to complete. Could someone tell me what I am doing wrong? I am very new to java! Thanks!
Citation:
progress.setProgress(0);
this should be
progress.setProgress(jumpTime);
I suppose?
Also I guess, this will lead to a problem. You cannot access UI-Components from background Threads. You need to use a Handler. See this Example.
I want to create a small android app that would show the system time in periodic intervals after clicking on a button ( i.e. setting the activity up )...The code for button creation and setting the periodic activity via Intent goes like this :
package com.example.timeupdate;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
Button button;
TextView show;
#Override
protected void onCreate(Bundle I_Love_Biriyani) {
super.onCreate(I_Love_Biriyani);
setContentView(R.layout.activity_main);
button = (Button) findViewById (R.id.pressButton);
show = (TextView) findViewById (R.id.Show);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
Intent openTimeUpdater = new Intent("com.example.timeupdate.TIMEUPDATER");
startActivity(openTimeUpdater);
}
});
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
finish();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
And here is the code for repeating the timer( for say 5 seconds ) where I used TimerTask class to perform the job :
package com.example.timeupdate;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class TimeUpdater extends Activity {
TextView Show;
TimerTask timer= new TimerTask(){
#Override
public void run() {
// TODO Auto-generated method stub
Date d = new Date();
Show.setText(""+d);
}
};
#Override
protected void onCreate(Bundle hotovaga) throws IllegalStateException {
// TODO Auto-generated method stub
super.onCreate(hotovaga);
setContentView(R.layout.new_update);
Show = (TextView) findViewById (R.id.time);
Timer t = new Timer();
t.scheduleAtFixedRate(timer , 0 , 5000);
}
}
After clicking on the button the time is shown only once then application is getting stopped showing a dialog-message. Need explanations to do this job in the same fashion.
You are trying to access an UI element inside non-UI thread.
Show.setText(""+d);
Instead, wrap it up in runOnUiThread interface to get proper output.
Use below code for your TimeUpdater class
public class TimeUpdater extends Activity {
TextView Show = null;
Calendar c;
int seconds;
int minutes;
int hours;
TimerTask timer= new TimerTask(){
#Override
public void run() {
c = Calendar.getInstance();
seconds = c.get(Calendar.SECOND);
minutes = c.get(Calendar.MINUTE);
hours = c.get(Calendar.HOUR);
runOnUiThread(new Runnable() {
#Override
public void run() {
Show.setText(hours + ":" + minutes + ":" + seconds);
}
});
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.new_update);
Show = (TextView) findViewById (R.id.time);
Timer t = new Timer();
t.scheduleAtFixedRate(timer , 0 , 5000);
}
}
Using an actual Timer (java.util.Timer) in conjunction with runOnUiThread() is one way to solve this issue, and below is an example of how to implement it.
public class myActivity extends Activity {
private Timer myTimer;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
myTimer = new Timer();
myTimer.schedule(new TimerTask() {
#Override
public void run() {
TimerMethod();
}
}, 0, 1000);
}
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.runOnUiThread(Timer_Tick);
}
private Runnable Timer_Tick = new Runnable() {
public void run() {
//This method runs in the same thread as the UI.
// Set your textView data here.
//Do something to the UI thread here
}
};
}
use PeriodicTask from Play-Service, it is the newest tool from Google to schedule a job background.