I have been looking all over the Android Studio Docs and StackOverflow, but I can not seem to find a way to do the equivalent of what is done in IOS here:
Adding Delay In A For Loop Without Blocking UI
in Android.
I have tried to use a Handler, but instead of running the code like:
// Iteration 1
// Delay of 500ms
// Iteration 2
// Delay of 500ms
// ...
The code seems to be run as follows:
// Iteration 1
// Iteration 2
// Delay of 500ms
// next state
The code I am using is stuctured like this:
Handler myHandler = new Handler();
while (someCondition) {
myHandler.postDelayed(new Runnable() {
public void run() {
myFunction();
}
}, 500);
}
The behavior I see when running this Activity is that is skips it, and after 500ms it goes to the next Activity (with expected result) but without showing the user what's happening.
How would I delay the loop so the user can see what's happening?
To clarivy:
The current state (after moveCard()) needs to be shown for x ms before going though the logic of my while loop again.
This is done untill the final state is has been reached.
Then the next Activity is started.
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.horserace_game);
this.cardDeck = findViewById(R.id.deck);
this.game_ended = false;
this.deck = new Deck();
this.aces = new ArrayList<>();
this.acesPos = new ArrayList<>();
this.hidden = new ArrayList<>();
// Remove all Aces from deck and put them in aces
//resetDeck creates a deck ordered on Face (so Ace SDHS, Two SDHS etc, which is handy for this purpose.
this.deck.resetDeck(1);
for (int i = 0; i < 4; i++) {
this.aces.add(this.deck.removeCard(0));
// Add a new pos for card
this.acesPos.add(0);
}
// Shuffle the deck
this.deck.shuffleDeck();
// Get 7 cards from the top of the deck to put on the
// side of the track and place them face down on the screen
for (int i = 0; i < 7; i++) {
this.hidden.add(this.deck.removeCard(0));
}
// Whilst the game is still running, pick a card from the deck and move the ace with the same suit
while (!this.game_ended) {
// Pick the next card from the deck
this.next = this.deck.removeCard(0);
cardDeck.setImageResource(getCardImage(next));
// Find the ace with the same suit as this.next and set it's 'score'++
for (Card ace : this.aces) {
if (ace.getSuit().equals(next.getSuit())) {
this.acesPos.set(ace.getSuit().ordinal(), this.acesPos.get(ace.getSuit().ordinal()) + 1);
break;
}
}
// Final state: the last ace now has a pos >7, this ace is the result. start new Activity
if (this.acesPos.get(next.getSuit().ordinal()) > 7) {
this.game_ended = true;
Intent winner = new Intent();
winner.putExtra("winner",next.getSuit().ordinal());
setResult(RESULT_OK, winner);
finish();
// If the ace is not at it's final position, move it
// and pick a new card in the next iteration.
} else {
// Move card
myHandler.postDelayed(new Runnable() {
public void run() {
Log.e("run: ", "moveCard");
moveCard();
}
}, 500);
}
}
}
Maybe Creating Something like this should work,
protected static void startTimer() {
timer.schedule(new TimerTask() {
public void run() {
mHandler.obtainMessage(1).sendToTarget();
}
}, 500);
}
public Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
myFunction();
}
};
Consider using a Timer, a polling model where the timer will run independently and execute the code in its associated TimerTask based on the frequency you define.
Timer timer = new Timer();
timer.schedule (new TimerTask()
{
#Override public void run()
{
// your program logic here
if (game_ended)
return;
// Pick the next card from the deck
next = deck.removeCard(0);
.
.
.
}
}, 0, 500);
This will start a timer immediately, with 500ms delay between triggers. Each time the timer fires the run() method in TimerTask will execute.
Create the timer in onCreate, start/schedule it in onResume, stop in onPause. Inside the TimerTask run() method you'd move your program logic.
moveCard() and wait to move to new intent:
while (!this.game_ended) {
// Pick the next card from the deck
this.next = this.deck.removeCard(0);
cardDeck.setImageResource(getCardImage(next));
// Find the ace with the same suit as this.next and set it's 'score'++
for (Card ace : this.aces) {
if (ace.getSuit().equals(next.getSuit())) {
this.acesPos.set(ace.getSuit().ordinal(), this.acesPos.get(ace.getSuit().ordinal()) + 1);
break;
}
}
// Final state: the last ace now has a pos >7, this ace is the result. start new Activity
if (this.acesPos.get(next.getSuit().ordinal()) > 7) {
this.game_ended = true;
myHandler.postDelayed(new Runnable() {
public void run() {
Intent winner = new Intent();
winner.putExtra("winner",next.getSuit().ordinal());
setResult(RESULT_OK, winner);
finish();
}
}, 500);
// If the ace is not at it's final position, move it
// and pick a new card in the next iteration.
} else {
// Move card
Log.e("run: ", "moveCard");
moveCard();
}
}
Related
I'm working on a simple memory game where you basically match the same cards. I want the cards to be able to flip back if both didn't match. The flipping codes are the following:
mT1.unselect();
mT2.unselect();
When i put it in the else statement the cards flip back immediately so i used a handler to slow it down.
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
mT1.unselect();
mT2.unselect();
playSound( SOUND_FAILED );
}
}, 2000);
But when i test it doesn't flip back, but the sound is played after the given time. What's the problem?
Full code:
public void onPosition(int position)
{
if (position == mLastPosition)
{
return;
}
mLastPosition = position;
Tile tile = mList.get(position);
tile.select();
int sound = tile.mResId % mSounds.length;
playSound(sound);
switch (mSelectedCount)
{
case 0:
mT1 = tile;
break;
case 1:
mT2 = tile;
if (mT1.getResId() == mT2.getResId())
{
mT1.setFound(true);
mT2.setFound(true);
mFoundCount += 2;
playSound(SOUND_SUCCEED);
}
else
{
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
mT1.unselect();
mT2.unselect();
playSound( SOUND_FAILED );
}
}, 2000);
}
break;
case 2:
if (mT1.getResId() != mT2.getResId())
{
}
mSelectedCount = 0;
mT1 = tile;
break;
}
mSelectedCount++;
mMoveCount++;
updateView();
checkComplete();
}
As with your previous question, you've still not given us enough context, but I'll have a wild stab in the dark - maybe you need to call:
updateView();
in your Runnable's run() method.
Maybe if you post your updateView() method and some more context of how it works, we might be able to help more if this guess doesn't fix it.
I am having trouble delaying the method assign_backgrounds() within a for loop. I am trying to create a Simon says game, but instead of delaying and showing the next button that "Simon" presses, it shows all the buttons at once. Any help here would be greatly appreciated. Thanks.
boolean simonsTurn = true;
int x = 4;
int s;
int delay = 1000;
int array_values[] = new int[]{1,2,3,4};
public void simonSays() {
// running = true;
if (simonsTurn == true) {
go();
for (int i = 0; i < x; i++) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
go();
}
}, 1000);
}
}
}
public void go(){
s = random_int_between(0,3);
assign_backgrounds(array_values[s]);
}
public void assign_backgrounds( int array_values ){
Handler handler = new Handler();
if( array_values == 1){
button1_.invalidate();
button1_.setBackgroundResource(R.drawable.goatclicked);
button1_.refreshDrawableState();
handler.postDelayed(new Runnable(){
public void run(){
button1_.invalidate();
button1_.setBackgroundResource(R.drawable.goat);
button1_.refreshDrawableState();}}, 1000);
}
else if( array_values == 2){
button2_.invalidate();
button2_.setBackgroundResource(R.drawable.pigclicked);
button2_.refreshDrawableState();
handler.postDelayed(new Runnable(){
public void run(){
button2_.invalidate();
button2_.setBackgroundResource(R.drawable.pig);
button2_.refreshDrawableState();}}, 1000);
}
else if( array_values == 3){
button3_.invalidate();
button3_.setBackgroundResource(R.drawable.chickenclicked);
button3_.refreshDrawableState();
handler.postDelayed(new Runnable() {
public void run() {
button3_.invalidate();
button3_.setBackgroundResource(R.drawable.chicken);
button3_.refreshDrawableState();}}, 1000);
}
if( array_values == 4) {
button4_.invalidate();
button4_.setBackgroundResource(R.drawable.cowclicked);
button4_.refreshDrawableState();
handler.postDelayed(new Runnable(){
public void run(){
button4_.invalidate();
button4_.setBackgroundResource(R.drawable.cow);
button4_.refreshDrawableState();}}, 1000);
}
}
It's because you are creating handlers very fast and then they are all starting at the same time. You should look into how Handler's work and about Asyncronous/Background tasks.
Now back to your problem, you are calling the a loop and it is creating handlers all in a row and they are being created very fast (nanoseconds). They will then all launch 1 second from that creation time because of your postDelayed() call. This is why everything is popping up at the same time! All of these delay posts are being executed at almost the same time on concurrent background threads.
Instead of a for(int i,...) loop you want to have a global int i, just add it to the top of the file.
At the end of any of Simon's turn you'll want, inside of the if, else if statement inside assign_background (at the end of the Runnables, then you'll want to call go().
This might cause problems because you are trying to access the main thread from all these background threads. so you might have to call the function runOnMainUIThread() as a quick hack when you call the go function.
All in all, you are going to have some problems until you understand Handlers, Background Processes, and Threads. Definitely great knowledge to learn about and Android has solid documentation on it just FYI.
I'm developing a game where LittleMan can move from block to block, and certain blocks are moving. I'm trying to detect when he moves to a new moving block, if that block is beneath him or if it has moved. I'm using Andengine's TimerHandler to check every 0.1 seconds but it is not working. Here's the code:
private void OnMovingBlock(final int CurrentPosRow, final int CurrentPosColumn) {
this.getEngine().registerUpdateHandler(new TimerHandler(0.1f, true, new ITimerCallback() {
#Override
public void onTimePassed(final TimerHandler pTimerHandler) {
if (LittleManPos[0] == CurrentPosRow && LittleManPos[1] == CurrentPosColumn) {
if (!LittleMan.collidesWith(MapRectangles[CurrentPosRow][CurrentPosColumn])) {
RestartScene();
}
}
}
}));
}
It seems he can move to the block and sit there with it moving in and out beneath him but it doesn't call RestartScene() UNTIL I move him again. Any idea where I am going wrong? Or is there another way to do this?
Why don't create a Timer?
TimerTask task = new TimerTask(){
#Override
public void run(){
// your code goes here
}
};
Timer timer = new Timer();
timer.sechedule(task, 0, 100); // 100 --> 0.1 second
To cancel the Timer, call timer.cancel();.
I've been working on a simple game using a Java Applet in which the player's goal is to get as many points as possible within a 30 second timeframe. Right now, I'm using a Swing timer to count down from 30 seconds, and once the 0 mark is reached, a "game over" screen is displayed with the player's score. I have these instance variables:
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
screenState = 0;
repaint();
}
};
Timer displayTimer = new Timer(30000, listener);
When the player clicks the "play" button, I execute displayTimer.start();.
Then, I have this within the appropriate case in my paint class:
g.drawString("Time Remaining: " + displayTimer.getDelay()/1000, 650, 100);
So, obviously, right now it's just displaying a static "Time Remaining: 30", and the screens switches after 30 seconds. What I'm trying to figure out is how I can repaint this value every one second so that it's a live timer. The only help I've been able to find thus far is for people use components.
ActionListener listener = new ActionListener() {
int count = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (count++==30) {
screenState = 0;
}
repaint();
}
};
Timer displayTimer = new Timer(1000, listener); // make it 30 times faster
You can use the a Thread that sleep it every one second using the sleep method of it
Here is a little sample that count to 30 with 1 second interval
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
int count = 0;
while(true)
{
if(count == 30) //end at 30 second
break;
try {
Thread.sleep(1000);
System.out.println("updated");
++count;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
This question already has an answer here:
How do you have the code pause for a couple of seconds in android?
(1 answer)
Closed 8 years ago.
In my layout, I have TextViews that are initially set to be invisible. When the user presses the start button, I want one of these TextViews to be visible for .8 seconds. Then I want it to become invisible again and show another TextView for .8 seconds and so on. So basicly, it's like(textView1 pops up for a bit then disappears, then textView2 pops up for a bit then disappears...). There needs to be a good amount of time between each textView. My problem is finding a way to make my code wait a bit in between the:
findViewById(R.id.textView1).setVisibility(00000000); //makes it visible
and
findViewById(R.id.textView1).setVisibility(00000004); //makes it invisible
I have tried many different methods, but most either makes the textView stay invisible, because it gets to the .setVisibility(00000004) to quickly, or it pauses the code, but never seems to become visible as if .setVisibility(00000000) was ignored. This is what I am trying currently, but the textViews stay invisible.
int counter=0;
public void GamePlay(View view)
{
turns[counter]=(int)(4*Math.random()+1);
counter=0;
while(turns[counter]!=0)
{
if(turns[counter]==1)
{
findViewById(R.id.textView1).setVisibility(00000000);
HoldGame();
findViewById(R.id.textView1).setVisibility(00000004);
}
else if(turns[counter]==2)
{
findViewById(R.id.textView2).setVisibility(00000000);
HoldGame();
findViewById(R.id.textView2).setVisibility(00000004);
}
else if(turns[counter]==3)
{
findViewById(R.id.textView3).setVisibility(00000000);
HoldGame();
findViewById(R.id.textView3).setVisibility(00000004);
}
else if(turns[counter]==4)
{
findViewById(R.id.textView4).setVisibility(00000000);
HoldGame();
findViewById(R.id.textView4).setVisibility(00000004);
}
counter++;
}
}
public void HoldGame()
{
Timer timer=new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
}
}, 1*1000);
}
You can try handler class -
Handler handlerTimer = new Handler();
int count = 0;
Runnable runnable = new Runnable(){
public void run() {
if(count==0)
findViewById(R.id.textView1).setVisibility(00000004);
else if(count==1)
findViewById(R.id.textView2).setVisibility(00000004);
else if(count==2)
findViewById(R.id.textView3).setVisibility(00000004);
else if(count==3)
findViewById(R.id.textView4).setVisibility(00000004);
count++;
if(count<4)
handler.postDealyed(runnable, 8000)
}};
Or you can you use ScheduledThreadPoolExecutor
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
int count=0;
scheduler.scheduleAtFixedRate
(new Runnable() {
public void run() {
if(count==0)
findViewById(R.id.textView1).setVisibility(00000004);
else if(count==1)
findViewById(R.id.textView2).setVisibility(00000004);
else if(count==2)
findViewById(R.id.textView3).setVisibility(00000004);
else if(count==3)
findViewById(R.id.textView4).setVisibility(00000004);
count++;
}
}, 0, 8, TimeUnit.SECONDS);
For more information