I have made the beginning of what will be a pretty cool wear app, for now it is just a metronome that vibrates on the tempo the user selects.
My problem is that when you exit the app or when the screen goes into "ambient mode" the vibration keeps going, which is great, but when you open the app again it opens a new "instance" (or however you say that) of the app so the vibration tempo previously selected keeps vibrating and when you select a new one you basically have 2 tempos vibrating.
Maybe this has something to do with the fact that I use a new Thread for the timing and that Thread keeps running. Is there a way to prevent this? Thank you!
public void buttonTempoOnClick(final View v) {
Running = !Running;
Button button = (Button) v;
Thread Timer = new Thread(new Runnable() {
public void run() {
while (Running == true) {
vibrate(150);
try {
Thread.sleep(60000 / Tempo);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Timer.start();
(vibrate refers to a void I created in the class, which works.)
public void vibrate(int duration){
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(duration);
}
Edit: I already have a public boolean Running that is false by default, when the "start' button is pressed it toggles the boolean and starts a new threat with a while(Running == true) loop in it. Stopping the vibration by pressing the button one more time works like a charm.
Try setting a variable in the class managing the thread, like
boolean isTempoOn = true;
In your main activity, in the onResume() function, check if it's already running. If so, don't start again.
Perhaps posting the applicable portion of code will help.
Related
I need to add an artificial pause before my AI plays its turn on the tic tac toe board. However, when I try to use something like Thread.sleep, in the location where I have the comment, the entire onClick function lags. What I mean by lagging is that the ((Button) v).setText("0") does not set the button text to 0 for the amount of time I use the sleep function and the moment that the timer is up, everything happens immediately. It is like the lag happens in the beginning of the function rather than the middle where the comment is. Is there anyway to address this problem or explain why the sleep function isn't lagging where it is suppose to be?
public void onClick(View v) {
if(!((Button) v).getText().toString().equals("")){
return;
}
((Button) v).setText("0");
turn();
// Need a pause before tictacAI does anything
tictacAI("0", "X").setText("X");
turn();
if(won){
resetBoard();
won = false;
}
}
When using Thread.sleep in onClick, you are actually blocking the entire UI Thread, This will prevent Android Framework redraw your view, so your change to View's property will not be displayed.
In order to delay the execution, you should use View.postDelayed, which will post your action to a message queue and execute it later.
public void onClick(View v) {
if(!((Button) v).getText().toString().equals("")){
return;
}
((Button) v).setText("0");
turn();
// Need a pause before tictacAI does anything
v.postDelayed(new Runnable() {
#Override
public void run() {
tictacAI("0", "X").setText("X");
turn();
if(won){
resetBoard();
won = false;
}
}
}, 1000L); //wait 1 second
}
If you are using Java 1.8, you can use lambda instead of anonymous class.
Another note:
this Runnable will not be garbage collected until executed, so a long interval may cause your entire view leaked, which is very bad. Consider using View.removeCallbacks when this callback is not needed anymore(like activity's onDestroy, for example)
Sorry for the terrible title, I am bad at describing these things.
I am building a metronome and have a (-) UI button that decreases the tempo by 1, and a (+) UI button that increases the tempo by 1.
My problem currently is that whenever I press either buttons, the metronome restarts itself since there's a new tempo, and plays immediately. So if you press the (-) button 10 times in a row, each time you press it you hear the initial metronome "beep".
I would like my app to do the following:
When the user clicks either (-) or (+) buttons, wait for 200 milliseconds
IF the user didn't click the buttons again in that timeframe, play the metronome
If the user DID click the button again, don't play the metronome, repeat the process: wait 200 milliseconds, if no click was made play the metronome, etc
The end result would be that if I'm at 100 bpm and I repeatedly press the (+) button 20 times until I am at 120 bpm, the metronome wouldn't start playing until I am done tapping.
How do I go about implementing this? Thank you!
Declare and instantiate the below in your activity:
private Handler timeoutHandler = new Handler();
private Runnable delayStartThread = new Runnable() {
public void run() {
startMetronome();
}
};
Then insert the below code block in your onClickListener for both + and - buttons:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
timeoutHandler.removeCallbacks(delayStartThread);
tempoOfMetronome++; //tempoOfMetronome--; for decrease button
stopMetronome();
timeoutHandler.postDelayed(delayStartThread, 200);
}
});
For more details on how the code works, refer the below links for examples (I used these examples to formulate the answer):
Android: clicking TWICE the back button to exit activity - How to use handler.postDelayed()
How to cancel handler.postDelayed? - How to cancel handler.postDelayed()
You should also look at the Android documentation for those methods.
If you want a delay between the action and the effect, there are several ways you can achieve it. This is one.
private boolean pressedAction = false;
#override
public void onClick(View v) {
if (pressedAction) return;
pressedAction = true;
new Thread(new Runnable(
#override
public void run() {
try {
Thread.sleep(200); // 200 miliseconds
} catch (Exception e) {}
// Update views or do work (program logic)
pressedAction = false;
}
}
}
Then, the metronome logic is your bussiness.
I have an app with a title screen. When the app first starts, I have an onCreate method that contains the following code:
setContentView(R.layout.title_screen);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
setContentView(R.layout.main_screen);
}
}, 2000);
When I run my app and press the back button while on the main_screen layout, it closes the app (as it should). However, when I reopen the app, it displays the title_screen layout for two seconds again even though the app is already running. How can I prevent this?
This will prevent the delay appearing again when resumed:
private static boolean flag = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!flag){
setContentView(R.layout.title_screen);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
setContentView(R.layout.main_screen);
}
}, 2000);
flag = true;
} else {
setContentView(R.layout.main_screen);
}
}
Btw, if your app was on background and it is calling onCreate again while being resumed, it means that it is killed by the OS. Therefore it is normal to have the initial delay again.
What I would do is to implement two different activities first one showing title_screen and the second which is started after 2s should show your main screen.
After looking at your code I can see that you ALWAYS start with title_screen then after 2s, you change to main_screen. Therefore, when you press back, that means you finish your activity. When you re-open your app, onCreated is called again, and it run every line of code as the previous opening.Of course, there's no difference in 2 times you open your app. To overcome it, I recommend to use SharedPreference to store the flag to check main_screen or title_screen.
I'm making a little game and in it I have to check if a value is zero every second. When it is zero the game should stop and show a dialog instead.
As from now the game never ever shoud work until the app is reinstalled.
So, I have an timer with an timertask which executes a runOnUiThread.
Timer:
private void update(){
Timer timer = new Timer();
timer.schedule(new TimerTask(){
#Override
public void run() {
onChange();
}
},0,(1000* getResources().getInteger(R.integer.remove_speed_inSecond)));
}
runOnUiThread: (with try/catch to catch the exeption at this point but i want to fix and not just ignore it.)
private void onChange(){
runOnUiThread(new Runnable() {
#Override
public void run() {
try{
checkupifexpire();
}
catch (Exception ex) {
}
}
});
}
The Method where i show the dialog:
private void checkupifexpire() {
if(eat == 0 || drink == 0 || wash == 0 || care == 0){
dialog = new Dialog(this, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener()
{
#Override
public void onCancel(DialogInterface dialog)
{
GameEngine.this.finish();
}
});
dialog.setContentView(R.layout.activity_rip);
dialog.show();
}
}
Always when I press the back button or just the home button then the App crashes.
Any Idea how to fix this?
So, the logcat tells us that is crashes on line 306 of GameEngine.java, in the method checkupifexpire, which looks like it is the dialog.show() line.
I'm not 100% sure, but from what you've said, it would seem to me that when back or home is pressed, the app will lose its UI thread. This means that checkuponexpire cannot do what it does.
To solve your crash problem, there are three obvious options:
You could use onPause in your main activity to catch when the app loses the screen. At this point you need to either stop the timer, or switch it to using Toast to communicate information.
Only use Toast in checkuponexpire
Decide that when the back or home is pressed the game is over anyway and cancel the Timer.
To Actually get the dialog, it may also be helpful to change the context you use to create the dialog with. Although it should be used sparingly, it may be that getApplicationContext() is what you need here (possibly this.getApplicationContext()).
Thanks to Neil Townsend and WELLCZECH. :)
My problem was the Lifecycles.
Mostly i had the App running in the onCreat() and had no onStart() method.
Just didn't know that thies methods were as much important as they are.
Also i didn't need a dialog shown. Instead i just have to start a new activity and cancel the old one.
I have written a function to create a splash screen with a 5 second timeout for my app.
The code works fine, but when the timeout reaches zero and I want to redirect to my main activity, the app crashes with the following error:
Only the original thread that created a view hierarchy can touch its views.
So I looked around a bit and someone suggested nesting this inside my function. It seems like a good Idea, but now methods like sleep / stop won't work.
My code is below, I can provide more / explain more in details if it isn't clear enough just let me know. Thanks for the help.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showSplashScreen();
}
protected boolean _active = true;
protected int _splashTime = 5000; // Splash screen is 5 seconds
public void showSplashScreen() {
setContentView(R.layout.splash_layout);
// Thread splashThread = new Thread() {
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
int waited = 0;
while (_active && (waited < _splashTime)) {
Thread.sleep(100);
if (_active) {
waited += 100;
}
}
} catch (InterruptedException e) {
// do nothing
} finally {
showApplication();
}
}
});
}
Probably not what you want to hear, but you should never put a splash screen on your mobile app. With the exception of games, when people use a mobile app they want to get in, do what ever it is they need to do, and get out. If you make that process take longer, people are just going to get frustrated with you app. You should probably reconsider just not using a splash screen.
This will perform sleep on the UI thread. That's never a good idea.
Why not something like this?
new Handler().postDelayed(new Runnable() {
public void run() {
// start application ...
}
}, _splashTime);
But this answer has a good point. Displaying a splash screen for 5 seconds can be very annoying.
I believe you want AsyncTask for this. The method called on completion of the task will be called on your UI thread, making modifying UI elements much easier.
Use a Handler to post an event to the UI thread that will remove the splash.
Code should be something like...
splash.show()
new Handler().postDelayed(
new Runnable() {
void run() {
splash.remove();
},
delayTime);
I suggest you to make new activity for your spalsh screen, show it in a regular way (with startActivityForResult) and place in it such code (in it, not in your main activity):
new Handler().postDelayed( new Runnable()
{
public void run()
{ finish(); }
}, 5000 );
Also you can handle in this new activity click events for giving opportunity to user to close it faster, tapping on it.