2009-09-22 12 views
13

Estoy tratando de hacer un juego para Android.¿Por qué no es view.invalidate? Volver a dibujar inmediatamente la pantalla en mi juego de Android

Tengo una clase de juego que extiende la actividad y maneja todas las entradas del usuario. Luego tengo una clase missionView que extiende la vista y dibuja el nivel en la pantalla.

Cuando el usuario hace clic en una puerta, quiero agregar algo de animación.

Lo que sucede es: El juego se llama door.open. Cambia el estado para que la función view.onDraw dibuje la puerta medio abierta. El juego llama a view.invalidate, que debería volver a dibujar la pantalla. Entonces el juego duerme por medio segundo. Luego llama a door.open otra vez. La segunda vez que se llama a la función, cambia el estado para que la función view.onDraw dibuje la puerta completamente abierta. Luego el juego llama a view.invalidate nuevamente.

El problema es que no vuelve a dibujar la pantalla cuando llega a view.invalidate. Si establezco un punto de interrupción en la línea y ejecuto el depurador y hago clic en "entrar", no entra en mi función view.onDraw. Ni siquiera puede mostrarme el código que está ejecutando.

Lo que tengo es: Clase Puerta:

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; 
    } 
} 

clase Juego:

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

He leído esta discusión, pero no había entendido lo que tengo que hacer para que mi código funcione. No había entendido cómo usar "Manejador" y "Mensaje". –

+0

Es posible que desee echar un vistazo a la discusión relacionada en http://stackoverflow.com/questions/2801877/android-why-wont-invalidate-update-my-buttons-immediately –

Respuesta

22

Ver # invalidate le dice al sistema que volver a dibujar (a través de onDraw) la vista tan pronto como el hilo principal queda inactivo. Es decir, al llamar a las agendas invalidadas, su vista se volverá a dibujar después de que haya finalizado todo el trabajo inmediato. Si el código en su clase de Juego se llama desde el hilo principal y lo pone a dormir, no solo está pausando el renderizado, sino que está pausando el procesamiento de entradas todo junto (generalmente una mala idea). Como regla general, nunca duermas un hilo que no engendraste a menos que sepas lo que estás haciendo.

Si desea que su lógica de juego retrase periódicamente el uso de Thread # sleep, ejecútelo en un hilo separado y use view.postInvalidate() para señalar el hilo principal para que se active y llame a onDraw.

+2

Solo para agregar ... a menos que haga su propios hilos todo su código se ejecuta en el hilo de UI. Si duerme o realiza una gran cantidad de cálculos en este hilo, experimentará una IU con menor capacidad de respuesta y, finalmente, obtendrá un cuadro de Aplicación que no responde (ANR). – Will

14

Para juegos, realmente usaría un acceso de nivel inferior a los gráficos. El LunarLander de Google es un excelente ejemplo para eso. Básicamente, usted tiene que hacer dos cosas:

  • lugar de uso Ver SurfaceView
  • controlador recibe de la SurfaceView
  • reemplazar view.invalidate() con repinte método personalizado() que contendría

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

método de pintura contendrá lo que tiene en View.onDraw en el momento

+0

He hecho lo que dijiste. Funciona ... más o menos. Las puertas se animan ahora, pero ahora los gráficos fallan cuando me desplazo por la pantalla. Tengo un oyente de gestos en mi clase de juego que vuelve a dibujar la pantalla cuando el usuario se desplaza con el dedo. Te permite desplazarte por el mapa. Solía ​​dibujar bien. Pero ahora obtengo la mitad de la pantalla dibujada y la otra mitad no se ha actualizado durante medio segundo antes de que se corrija. –

+2

El uso de diferentes clases de vista no resuelve este "problema". Es necesario entender que NUNCA JUGAR NUNCA se ponga en espera en el hilo principal y que la invalidación no active el dibujo inmediatamente, sino que "recuerde" las regiones que necesitan volver a dibujar la próxima vez que el hilo de la interfaz de usuario tenga la oportunidad de hacerlo. – Zordid

0

He enfrentado el mismo problema en mi aplicación.

En mi caso yo estaba tratando de llamar invalidate() del método de la [AsyncTask][2][doInBackground()][1].

El problema se solucionó moviendo invalidate() al método [onProgressUpdate()][3] de AsyncTask. Es decir.no puede actualizar la interfaz de usuario del método doInBackground() de AsyncTask. Se menciona en los documentos de Android, pero es muy fácil olvidarlo ... así que no olvides consultarlo.

Cuestiones relacionadas