Android: how to create delay in loop while still updating the UI

22,235

Use a Handler with a Runnable (which does one iteration of the loop in its run()) and postDelayed(Runnable r, long delayMillis) for the delay. Count the iterations so you know when to stop and use the last one to removeCallbacks() and call a method to do whatever needs to be once the dealing is done.


Something like

private final Handler mHandler = new Handler();
private int iLoopCount = 0;

private final Runnable rDealAndWait = new Runnable()
{
  public void run()
  {
    if (dealtAHand())
    {
      ++iLoopCount;
      mHandler.postAtTime(this, SystemClock.uptimeMillis() + DEAL_DELAY);
    }
    else
    {
      doAfterAllHandsDealt();
    }
  }
};


private boolean dealtAHand()
{
  if (i == CARD_COUNT) return false;

  //human player
  LoadCard(playerCards.get(iLoopCount), pCards.getCard(i));
  playerCards.get(iLoopCount).setVisibility(View.VISIBLE);
  playerCards.get(iLoopCount).setPadding(1, 1, 1, 1);

  // etc

  return true;
}

And then in the onCreate or wherever

  dealtAHand();
  mHandler.postAtTime(rDealAndWait, SystemClock.uptimeMillis() + DEAL_DELAY);

}

Share:
22,235
Daniel
Author by

Daniel

Updated on July 09, 2022

Comments

  • Daniel
    Daniel almost 2 years

    This is my first post here so sorry if I don't format my question correctly. I am developing my first Android app and it is a card game. I've developed the same card game in C# using visual studio. In C#, in order to simulate action and delay when dealing, etc, I slept the thread for a given amount of time, then called the method Application.DoEvents() which forced the UI to update. However, in java, I cannot seem to find an answer to this problem.

    I'll post some code from my deal() method, which I need to delay so it looks like the cards are actually being dealt in a circle, and not all at once since the computer goes so fast:

    private void dealHelper() { Hand pCards = Referee.getHumanHand();

        //show all cards in rotating order
        for(int i=0; i<CARD_COUNT; i++)
        {
            ///////////////////////////////////////////////////////////////////////
            // load each card into their appropriate ImageViews and make visible //
            ///////////////////////////////////////////////////////////////////////
    
            //human player
            LoadCard(playerCards.get(i), pCards.getCard(i));
            playerCards.get(i).setVisibility(View.VISIBLE);
            playerCards.get(i).setPadding(1, 1, 1, 1);
    
            // allow discarded cards to be clickable
            discardCardImages.get(i).setPadding(1, 1, 1, 1);
    
            //computer 1
            computer1Cards.get(i).setImageResource(R.drawable.cardskin);
            computer1Cards.get(i).setVisibility(View.VISIBLE);
    
            //computer 2
            computer2Cards.get(i).setImageResource(R.drawable.cardskin);
            computer2Cards.get(i).setVisibility(View.VISIBLE);
    
            //computer 3
            computer3Cards.get(i).setImageResource(R.drawable.cardskin);
            computer3Cards.get(i).setVisibility(View.VISIBLE);
        }
    }
    

    I need a slight delay of about 500ms between each card that is displayed on the screen to simulate action. I've searched and searched and haven't a solution that works (or I understand). Any help would be much appreciated.

    Thank You, Daniel

  • Daniel
    Daniel almost 13 years
    I've considered this solution, however I guess I didn't understand how it worked fully. When I tried to implement each card card deal into a Runnable, I get errors saying it can't access variables outside the method (such as i) and that 'i' isn't static? I need 'i' so the code knows which card to deal. I wasn't sure of a way around this.
  • Ben Williams
    Ben Williams almost 13 years
    You should be able to declare a private int i field in the surrounding class and use that in the runnable.
  • harism
    harism almost 13 years
    Another way would be to do whole for -loop in a separate thread. There you can sleep safely without blocking UI and shoot commands to update card visibilities. You just have to do it using runOnUiThread.
  • Daniel
    Daniel almost 13 years
    thanks for the tips @Ben and @harism, i'll try to implement your suggestions and let you know how it turns out!