2009-09-22 3 views
13

Je suis en train de faire un jeu Android.Pourquoi n'est pas view.invalidate immédiatement redessiner l'écran dans mon jeu Android

J'ai une classe de jeu qui étend l'activité et gère toutes les entrées de l'utilisateur. Ensuite, j'ai une classe missionView qui étend la vue et dessine le niveau sur l'écran.

Lorsque l'utilisateur clique sur une porte, je veux ajouter une animation. Qu'est-ce qui se passe est: Le jeu appelle door.Open. Modifie l'état de sorte que la fonction view.onDraw dessine la porte à moitié ouverte. Le jeu appelle view.invalidate, ce qui devrait redessiner l'écran. Ensuite, le jeu dort pendant une demi-seconde. Ensuite, il appelle à nouveau door.open. La deuxième fois que la fonction est appelée, elle change d'état, de sorte que la fonction view.onDraw dessine la porte complètement ouverte. Ensuite, le jeu appelle à nouveau view.invalidate.

Le problème est qu'il ne redessine pas l'écran quand il arrive à view.invalidate. Si je mets un point d'arrêt sur la ligne et que j'exécute le débogueur et que je clique sur le pas, cela ne marche pas dans ma fonction view.onDraw. Il ne peut même pas me montrer le code qu'il exécute.

Ce que j'est: classe de porte:

public boolean open() 
{ 
    if (doorState == DoorState.Closed) 
    { 
     doorState = DoorState.Opening; 

     return true; 
    } 
    else if (doorState == DoorState.Opening) 
    { 
     doorState = doorState.Open; 
     return true; 
    } 
    else 
    { 
     return false; 
    } 
} 

classe du jeu:

if (tile instanceof DoorTile) 
{ 
    DoorTile doorTile = (DoorTile) tile; 
    Door door = doorTile.getDoor(); 

    if (door.isClosed()) 
    { 
     door.open(); 
     selectedEntity.openDoor(); 
     view.invalidate(); // This line does not work 

     try 
     { 
      Thread.sleep(500); 
     } 
     catch (InterruptedException e1) 
     { 
      // TODO Auto-generated catch block 
      e1.printStackTrace(); 
     } 
     door.open(); 

     // Handled touch event so break switch statement 
     break; 
    } 
} 
+0

J'ai lu cette discussion, mais je n'avais pas compris ce que je devais faire pour que mon code fonctionne. Je n'avais pas compris comment utiliser "Handler" et "Message". –

+0

Vous pouvez jeter un oeil à la discussion connexe à http://stackoverflow.com/questions/2801877/android-why-wont-invalidate-update-my-buttons-immédiatement –

Répondre

22

Voir # invalidate indique au système de redessiner (via onDraw) la vue dès que le thread principal va au ralenti. C'est-à-dire qu'appeler invalidate planifie que votre vue soit redessinée après que tous les autres travaux ont été terminés. Si le code de votre classe Game est appelé à partir du thread principal et met ce thread en veille, vous ne faites pas que mettre le rendu en pause, vous mettez en pause le traitement des entrées tous ensemble (généralement une mauvaise idée). En règle générale, ne dormez jamais un fil que vous n'avez pas engendré à moins que vous ne sachiez ce que vous faites. Si vous souhaitez que votre logique de jeu retarde périodiquement l'utilisation de Thread # sleep, exécutez-la dans un thread séparé et utilisez view.postInvalidate() pour signaler au thread principal de se réveiller et d'appeler onDraw.

+2

Juste pour ajouter ... sauf si vous faites votre propres threads tout votre code s'exécute dans le thread UI. Si vous dormez ou faites beaucoup de calculs dans ce thread, l'interface utilisateur sera moins réactive et vous obtiendrez éventuellement une case ANR (Application Not Responding). – Will

14

Pour les jeux, j'utiliserais un accès de niveau inférieur aux graphiques. Le LunarLander de Google est un excellent exemple pour cela. Fondamentalement, vous devez faire deux choses:

  • au lieu de vue utilisation SurfaceView
  • gestionnaire get de la SurfaceView
  • remplacer view.invalidate() avec méthode personnalisée repeindre() qui contiendrait

    private void repaint() { 
        Canvas c = null; 
        try { 
        c = surfaceHolder.lockCanvas(); 
        paint(c); 
        } finally { 
        if (c != null) { 
         surfaceHolder.unlockCanvasAndPost(c); 
        } 
        } 
    } 
    

méthode de peinture contiendra ce que vous avez à View.onDraw au moment

+0

J'ai fait ce que vous avez dit. Ça marche ... en quelque sorte. Les portes s'animent maintenant, mais maintenant le graphique glitch quand je fais défiler l'écran. J'ai un gesturelistener dans ma classe de jeu qui redessine l'écran quand l'utilisateur fait défiler avec son doigt. Cela vous permet de faire défiler la carte. Il sert à bien dessiner. Mais maintenant, la moitié de l'écran est tirée et l'autre moitié n'a pas été mise à jour pendant une demi-seconde avant qu'elle ne se corrige. –

+2

L'utilisation d'une classe de vue différente ne résout pas ce "problème". Il est nécessaire de comprendre que JAMAIS on n'appelle sommeil dans le thread principal et que l'invalidation ne déclenche pas le dessin immédiatement mais "se souvient" des régions qui ont besoin d'être redessinées la prochaine fois que le thread UI aura une chance de le faire. – Zordid

0

J'ai été confronté au même problème dans mon application.

Dans mon cas, j'essayais d'appeler invalidate() à partir de la méthode [doInBackground()][1] du [AsyncTask][2]. Le problème a été résolu en déplaçant invalidate() à [onProgressUpdate()][3] méthode de AsyncTask. C'est à dire.vous ne pouvez pas mettre à jour l'interface utilisateur à partir de la méthode doInBackground() de AsyncTask. Il est mentionné dans les docs Android, mais il est si facile de l'oublier ... alors, n'oubliez pas de le vérifier.

Questions connexes