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)
Related
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 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.
I am using the android developer tools in Eclipse, programming in Java, and I need to make an object move across the screen as long as a button is pressed. I've been doing research for hours, and I cannot find any methods to accomplish this. I've tried running threads, which often crash or seemingly don't execute. I've also tried an onClickListener which reads the button state and uses it to determine whether or not the button is still pressed. I'm currently using a while loop, but this just freezes the program. I believe that this is the best method, and I've tried to use Thread.sleep in order to limit the number of iterations per second, as I believe that this is the reason it is freezing. Am I on the right track or am I way off in left field? Here is a snippet of code:
rightButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View arg0)
{
while(arg0.isPressed())
{
mover.updateCoordinates(1, 0);
}
}
});
Would you try this another method?
Firstly declare your button as class variable, declare a Handler and a Runnable:
private Button rightButton; // You will assign this in onCreate() method
private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
#Override
public void run() {
if(rightButton.isPressed())
{
// If press state is pressed, move your item and recall the runnable in 100 milliseconds.
mover.updateCoordinates(1, 0);
mHandler.postDelayed(mRunnable, 100);
}
}
};
Then your button's onClickListener will looks like this:
rightButton.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0)
{
// Instead of performing a loop here, just call a runnable, do simple press state checking there.
mHandler.postDelayed(mRunnable, 100);
}
});
Create a loop that updates the views, waits, and calls itself after it finishes waiting. Within the loop, have an animation if statement with a boolean field that move on true and does not move on false. Have the onClick method toggle the boolean field's value.
Try:
myView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
// Start your animation here
} else if (e.getAction() == MotionEvent.ACTION_UP) {
// Stop your animation here
}
return false;
}
});
References:
MotionEvent
OnTouchListener
Alternatively, override onKeyUp and onKeyDown of the View class and check for the KEYCODE_ENTER KeyEvent
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 am trying to set images with a 600ms delay using a countdown timer as follows:
protected void onCreate(Bundle savedInstanceState) {
int order[3];
index=0;
image1.setOnClickListener(this);
#Override
public void onClick(View arg0)
{
order={2,1,3};
do_switches();
}
private void do_switches()
{
new CountdownTimer(3*600, 600) {
public void onTick(long millisUntilFinished) {
switch(order[index]){
case 1:
image1.setImageResource(R.drawable.one);
break;
case 2:
image2.setImageResource(R.drawable.two);
break;
case 3:
image3.setImageResource(R.drawable.three);
break;
}
index++;
}
public void onFinish() {
}
}.start();
}
}
This is an example but in my actual code, I use larger arrays (about 50 in size). My problem is, the countdown timer does not always go all the way to the last spot in the array. Sometimes works but other times it stops before reaching the last point of my array. However if I increase the interval time to be 2000ms it always works. Its definitely some processing going on that delays this but I don't expect a countdown to not work just because I have reduced the interval. Can someone tell me what I can do to make sure the countdown timer does not stop before going through my whole array or is there a better way to do this.
I also tried using a timer with scheduleAtFixedRate but I got an error saying I cannot touch views in a thread because they were not constructed in the thread
Please help
Thanks
I'm sure there are other details involved and this may not cure everything, but for the last bit ("...cannot touch views in a thread..."), you need to pass a Runnable to Activity.runOnUiThread. In that Runnable, do your setting of image resources and anything else that will affect the screen.