Usar Timer en Android

Resuelto
matrix124 Mensajes publicados 136 Estado Miembro -  
scinarf Mensajes publicados 1183 Estado Miembro -
Hola, necesitaría ayuda en español, por favor.

15 respuestas

scinarf Mensajes publicados 1183 Estado Miembro 294
 
Hola,

Entonces no veo muy claro lo que quieren hacer... ¡muchas cosas extrañas!

Para recuperar el tiempo en Android hay que usar System.currentTimeMillis()

Luego para hacer lanzar un evento después de un cierto intervalo hacer:

long MyFuturAction = System.currentTimeMillis() + Delai;
while (System.currentTimeMillis() < MyFuturAction)
// Realizar la acción después de la espera.

Más información aquí: https://developer.android.com/reference/java/lang/System.html#currentTimeMillis%28%29

Doc oficial de Android, no duden en usarla

Después ¿están obligados a usar Inner Classes (clase definida directamente en el mismo .java que otra clase)? Encuentro que eso hace que el código sea completamente ilegible!

Para mover un objeto en la pantalla tipo Drawable o Bitmap, recomiendo usar un Thread asociado a un Handler, que es mucho más claro y limpio, a mi parecer.

Si tienen preguntas sobre cómo hacer, les invito a plantearlas.
1
matrix124 Mensajes publicados 136 Estado Miembro 57
 
Hola,

Antes que nada, gracias por tu respuesta.

Al ser realmente principiante, sé que mi código es malo. Sin embargo, no he tenido mucho tiempo para aprender a usar bien Java porque se trata de un proyecto que debo presentar para el Baccalauréat en el marco de la opción Informática y Ciencias de la Digital. Además, no he recibido ninguna formación.

Con respecto a mi aplicación, en realidad quiero ejecutar cierta acción cada 50 ms, por ejemplo, y no conocer el tiempo con un currentTimeMillis. En cuanto al hilo asociado a un handler, realmente no tengo ninguna idea de cómo implementarlo.

Me gustaría saber cómo mover bloques, como en este mini-juego (Line Runner): https://play.google.com/store/apps/details?id=com.djinnworks.linerunnerfree&hl=fr

Sin embargo, como no tengo idea de cómo hacerlo, pensé que sería bueno usar un Timer, pero a priori esta técnica no parece adecuada. Si pudieras orientarme hacia una forma más apropiada de poder hacer que los bloques se desplacen en la pantalla como en el juego, te lo agradecería mucho.

Gracias de antemano.
0
scinarf Mensajes publicados 1183 Estado Miembro 294
 
Saben que no hay ningún problema en ser principiante ;) y hay que empezar algún día.

Veo más o menos lo que ustedes quieren hacer:

public class Bloc {

private final String TAG = "Bloc";

private int pX=775;
private int pY=265;
private int Repete;

private RectF mRectangle = null;

public Bloc()
{
mRectangle = new Rectangle(pX,pY,pX+10,pY+10);//Rectángulo de 10 píxeles

}

public void deplaceRect (int pas, int time)
{
while (Repete < 50)
{
mRectangle.set(mRectangle.left + pas, mRectangle.top + pas, mRectangle.right + pas, mRectangle.bottom + pas)
Repet +=1;
Try{
Thread.sleep(time);
}catch(Exception e)
{
Log.d(TAG, "Erreur sur thread sleep "+e);
}
}
}

public rectangle getRectangle()
{
return mRectangle;
}

}

Esta clase permite crear un rectángulo, una vez se llame al método deplaceRect, se repetirá 50 veces y moverá el rectángulo en la "paso" utilizado todas las "veces" de tiempo.

Luego necesitará un objeto para dibujar y en el método deplacerect puede hacer Objetadessinar.setBounds(mRectangle);
Objetadessinar.invalidate();

el objeto será dibujado durante 50 veces. (se puede cambiar el 50 por otra condición, como mover el objetoadessiner mientras no salga de la pantalla o durante un cierto tiempo, o si estáis haciendo un juego, mientras el jugador no haya perdido, todo depende de vuestros criterios)

No duden en preguntar si desean más información
0
matrix124 Mensajes publicados 136 Estado Miembro 57
 
Una vez más me gustaría agradecerle el tiempo que me concede y su apoyo.

Espero que no piense que no he reflexionado o que sea por pereza que le hago las siguientes preguntas.

He entendido bien todo el código que me proporcionó, excepto Log.d(TAG, "Erreur sur thread sleep "+e); por eso en el código que sigue lo he puesto en comentario porque Eclipse me mostraba un error y no tenía idea de cómo resolverlo. En cuanto a los pasos y el tiempo, por ahora he puesto valores arbitrarios que modificaré más tarde observando el resultado.

Resulta que para realizar mi mini-juego he utilizado una estructura de Scratch/Zero donde sólo tenía que ocuparme del método Draw. Aquí tienes una parte:
public class MainSurfaceView extends SurfaceView implements SurfaceHolder.Callback { Bloc b = new Bloc(); @Override protected void onDraw(Canvas pCanvas) { // ¡Dibujar aquí! pCanvas.drawColor(Color.WHITE); int width = getWidth(); int largeur = width * 20 / 100; int mlargeur = width * 92 / 100; int height = getHeight(); int hauteur = height * 60 / 100; int taille = height * 6 / 100; mPaint.setColor(Color.BLACK); pCanvas.drawRect(0, hauteur, width, hauteur + taille, mPaint); pCanvas.drawRect(b.getRectangle(), mPaint); mPaint.setColor(Color.RED); pCanvas.drawRect(largeur, hauteur - taille, largeur + taille, hauteur,mPaint); } 


Sin embargo mencionó "Objetadessiner.setBounds(mRectangle); Objetadessiner.invalidate(); ", y aquí nuevamente no entiendo muy bien de qué se trata. Si pudiera aclararme para que entienda cómo voy a poder mostrar este Bloc que se desplaza.
Gracias.
0
scinarf Mensajes publicados 1183 Estado Miembro 294
 
Sin problema, sé que puede parecer bastante oscuro al principio.
Por cierto, no siempre estoy de acuerdo con el sitio “Zéro” (el sitio del cero), creo que complica las cosas más simples… como por ejemplo aquí.
¿Qué forma desean dibujar, un simple cuadrado para empezar? ¿Les va bien?
(quizá haya que adaptar el nombre del paquete)
Vamos a proceder en dos archivos .java diferentes, es mucho más claro:
El primero que contiene la MainActivity y que se llama de forma evidente MainActivity.java (cuando lo creen)

package com.example.testshape;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity
{
private myView v;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
v = new myView(this);
setContentView(v);
}

}

El segundo que contiene mi vista (View) con un Drawable, un objeto que permite ser trazado en la pantalla
(ya quizá habrá que cambiar el nombre del paquete). Este archivo se llama myView.java
para probar en mi HTC Desire he modificado los valores iniciales de pX y pY

package com.example.testshape;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class myView extends View
{
private final String TAG = "MyView";
private ShapeDrawable Sd =null;
private RectShape RS = null;
private int pX=100;
private int pY=100;
private int Repete;
private Rect mRectangle = null;

public myView(Context context)
{
// TODO Auto-generated constructor stub
super(context);
RS = new RectShape();
Sd = new ShapeDrawable(RS);
Sd.getPaint().setColor(Color.BLUE);
this.setBackgroundColor(0xff000000);
mRectangle = new Rect(pX,pY,pX+20,pY+20);//Rectángulo de 10 píxeles
Sd.setBounds(mRectangle);
invalidate();
}

public myView(Context context, AttributeSet attrs)
{
super(context, attrs);
// TODO Auto-generated constructor stub
}

public myView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}

public void deplaceRect (int pas, int time)
{
while (Repete < 50)
{
mRectangle.set(mRectangle.left + pas, mRectangle.top + pas, mRectangle.right + pas, mRectangle.bottom + pas);
Sd.setBounds(mRectangle);
invalidate();
Repete +=1;
try{
Thread.sleep(time);
}
catch(Exception e)
{
Log.d(TAG, "Erreur sur thread sleep "+e);
}
}
}

@Override
public void onDraw(Canvas canvas)
{
Sd.draw(canvas);
}
}

Invalidate est utilisé pour forcer le thread UI à redessiner les objets à l’écran. Pour utiliser la méthode deplaceRect il suffit de placer cette ligne dans l’Activity principale après le setContentView(v)

v.deplaceRect(5,15);

Le temps est en millisecondes donc vous ne verrez pas grand-chose avec 15 mais faites des essais.
0
matrix124 Mensajes publicados 136 Estado Miembro 57
 
Gracias, sin embargo, tras varias pruebas en mi teléfono y en mi emulador no obtengo una animación sino solo un cuadrado azul que se muestra sobre un fondo negro.
Por eso me preguntaba si el código funcionaba en su HTC Desire porque si aumento el tiempo en thread.sleep solo me toma más tiempo para mostrar el resultado del desplazamiento. Eso se traduce en una pantalla blanca durante varios segundos y luego la aparición del cuadrado azul sobre el fondo negro.
Creo que debería dejarle tranquilo por hoy al menos.
Gracias y buenas noches.
0
scinarf Mensajes publicados 1183 Estado Miembro 294
 
Para que la animación se inicie, hay que añadir:

V.deplace(5,100) después de la llamada al método setContentView(v)
0
matrix124 Mensajes publicados 136 Estado Miembro 57
 
Sí, ya está ahí y aun así no pasa nada.
0
scinarf Mensajes publicados 1183 Estado Miembro 294
 
Entonces lo mejor es hacer una activación al hacer clic en un botón, ¿por qué no el botón Menú? Voy a dejarlo listo.
0
matrix124 Mensajes publicados 136 Estado Miembro 57
 
De acuerdo, voy a continuar con algunas cosas sobre mi aplicación porque todavía me queda mucho trabajo.
0
scinarf Mensajes publicados 1183 Estado Miembro 294
 
Lo miro con más detenimiento este fin de semana si pueden esperar hasta entonces.
0
matrix124 Mensajes publicados 136 Estado Miembro 57
 
Claro, y que tengas una buena noche.
0
scinarf Mensajes publicados 1183 Estado Miembro 294
 
Hola, ¿bien descansado? Vuelvo a contactarte con algunas preguntas.

¿Qué tipo de interacción con el usuario deseas? ¿un botón de Inicio para iniciar la animación te parece bien? ¿o solo tocando la pantalla (da igual dónde)?
0
matrix124 Mensajes publicados 136 Estado Miembro 57
 
Buenas noches, al tocar la pantalla sería perfecto.
0
scinarf Mensajes publicados 1183 Estado Miembro 294
 
Voila he hecho algo que funciona y que tiene el mérito de estar super limpio!

Algunas convenciones que uso:

- 1 Clase = 1 archivo Java, así que no hay Inner Class
- Cada objeto tiene su destructor para evitar pérdidas de memoria importantes (no gestionadas por el GC)
- He comentado el código para que cada objeto sea claro y cada línea sea comprensible.

MainActivity
package com.example.testshape;

import android.app.Activity;
import android.os.Bundle;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class MainActivity extends Activity implements OnTouchListener
{
private myView v=null;
private Display display=null;//Obtención del tamaño de la pantalla para adaptar el programa a todos los tamaños

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
display = this.getWindowManager().getDefaultDisplay();
v = new myView(this, display);
setContentView(v);
}

//Acción a realizar cuando se toca la pantalla
//Se identifican dos acciones: colocar un dedo y mover un dedo
@Override
public boolean onTouch(View arg0, MotionEvent event)
{
// TODO Auto-generated method stub
int action = event.getAction();
if ( action == MotionEvent.ACTION_DOWN | action == MotionEvent.ACTION_MOVE)
{
v.onTouchEvent(event);
}
return true;
}

//Destructor para evitar leaks de memory
@Override
public void onDestroy()
{
v.onDestroy();
v = null;
super.onDestroy();
}

}

MyView

package com.example.testshape;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;

public class myView extends View
{

private final String TAG = "MyView";//Permite identificar la actividad en los logs
private ShapeDrawable Sd =null;//El objeto que se dibuja

private int pX=100;//Posición X al inicio del programa
private int pY=100;//Posición Y al inicio del programa
private Rect mRectangle = null;//Rectángulo utilizado para la posición inicial
private Movement myAnimation=null;//Hilo responsable de la animación del ShapeDrawable
private Display D=null;

public myView(Context context, Display d) //Constructor
{
// TODO Auto-generated constructor stub
super(context);
Sd = new ShapeDrawable(new RectShape());
Sd.getPaint().setColor(Color.BLUE);
this.setBackgroundColor(0xff000000);
mRectangle = new Rect(pX,pY,pX+20,pY+20);//Rectángulo de 10 píxeles
Sd.setBounds(mRectangle);
D = d;
myAnimation = new Movement(this, d);
}

public myView(Context context, AttributeSet attrs) //Constructor no utilizado
{
super(context, attrs);
// TODO Auto-generated constructor stub
}

public myView(Context context, AttributeSet attrs, int defStyle) //Constructor no utilizado
{
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}

//Método llamado para dibujar (o actualizar) la pantalla
@Override
public void onDraw(Canvas canvas)
{
Log.d(TAG, "Draw called"); //Log visible en el LogCat
Sd.draw(canvas); //Redibujar el ShapeDrawable
}

//Método llamado al pulsar la pantalla
@Override
public boolean onTouchEvent(MotionEvent event)
{
if (!myAnimation.getStatut()) myAnimation.start();//Si el Hilo no está iniciado entonces se inicia
else if (myAnimation.getfinish())//Si el Hilo ya se inició y ha terminado entonces se crea uno nuevo
{
myAnimation.onDestroy();
myAnimation = null;
myAnimation = new Movement(this,D);
}
return true;

}

//Getter para recuperar el ShapeDrawable
public ShapeDrawable getShapeDrawable()
{
return Sd;
}

//Destructeur llamado automáticamente para que no queden referencias o punteros no nulos al cerrar la activity
public void onDestroy()
{
myAnimation.onDestroy();
myAnimation = null;
mRectangle = null;
Sd = null;
}

}

movement

package com.example.testshape;

import android.graphics.drawable.ShapeDrawable;
import android.util.Log;
import android.view.Display;

public class Movement extends Thread
{
private final String TAG ="MovementThread";
private final int INVALIDATE = 0;
private int Width = 0;
private int X_ini = 0;
private int Y_ini = 100;
private ShapeDrawable SD = null;
private sdhandler UIHandler = null;
private boolean Start = false;
private boolean finish = false;

public Movement(myView v, Display d)
{
SD = v.getShapeDrawable();//Se obtiene el Drawable de la vista creada en la MainActivity
SD.setBounds(X_ini,Y_ini,X_ini + 20,Y_ini + 20);//para las necesidades del Thread se coloca el ShapeDrawable en el borde derecho de la pantalla
Width = d.getWidth();
UIHandler = new sdhandler(v);
UIHandler.sendEmptyMessage(INVALIDATE);//Se dibuja una primera vez el ShapeDrawable
}

//Método que permite ejecutar código en paralelo (y no en secuencia)
public void run()
{
Start = true;//Informamos al programa que el Hilo ya está iniciado para no lanzarlo dos veces
while (SD.getBounds().right < Width)//Criterio de parada mientras no se alcance el borde derecho, puede modificarse
{
//Un hilo no tiene la posibilidad de cambiar la interfaz gráfica por eso llamamos a un Handler que se ejecuta en el hilo principal
//Por lo tanto existe la posibilidad de cambiar la interfaz Gráfica
UIHandler.sendEmptyMessage(INVALIDATE);//Se comunica con el Handler enviando Mensajes de tipo int
try {
Thread.sleep(100);//Esperamos 0.1 segundos para que ShapeDrawable se redibuje puede modificarse para más rapidez
} catch (InterruptedException e) {
// TODO Auto-generated catch block
Log.d(TAG,"Erreur Sleep " + e);//Detección de error en el Thread.sleep obligatoria
break;
}
}
finish = true;
}

//Getter para el Estado del Hilo
public boolean getStatut()
{
return Start;
}
//Getter para saber si el Hilo ha terminado
public boolean getfinish()
{
return finish;
}

//Destructeur
public void onDestroy()
{
UIHandler.onDestroy();
UIHandler = null;
SD = null;
}
}

Handler

package com.example.testshape;

import android.graphics.drawable.ShapeDrawable;
import android.os.Handler;

//El Handler permite interactuar en los elementos del Thread Principal llamado Thread UI
public class sdhandler extends Handler
{

private myView mv=null;
private ShapeDrawable SD=null;

//Constructor
public sdhandler(myView v)
{
mv = v;
SD = mv.getShapeDrawable();
}

//Método que permite la comunicación entre el Thread y el Handler
public void handleMessage(android.os.Message msg)
{
switch (msg.what)
{
case 0:
{
SD.setBounds(SD.getBounds().left + 10,SD.getBounds().top +10,SD.getBounds().right +10,SD.getBounds().bottom +10 );//Se mueve el ShapeDrawable
mv.invalidate();//Solicita actualizar la pantalla
break;
}
default :
break;
}
}

//Destructeur
public void onDestroy()
{
mv = null;
SD = null;
}
}

Si tienes preguntas, no hay problema.
0
scinarf Mensajes publicados 1183 Estado Miembro 294
 
El nombre de los archivos Java debe ser el mismo que el nombre de las clases declaradas

Por ejemplo:
MainActivity
myView
movement
sdhandler
0
matrix124 Mensajes publicados 136 Estado Miembro 57
 
Magnífico, ¡funciona de maravilla! Muchas gracias por todo lo que has podido hacer para ayudarme, es realmente genial y es exactamente el resultado que esperaba obtener.
Realmente está muy bien detallado gracias a los numerosos comentarios que has podido dejar. Muchas gracias de nuevo.
0
scinarf Mensajes publicados 1183 Estado Miembro 294
 
Después de eso, ya haces el resto ;)

Pero no dudes en volver a contactarme en este foro, hago un pase regular. Y a partir de ahí puedes hacer bastantes cosas para divertirte.

Piensa en cambiar el estado del tema a resuelto ;)
0