Using Timer on Android

Solved
matrix124 Posted messages 136 Status Member -  
scinarf Posted messages 1183 Status Member -
```java
public class Bloc {
int pX = 775;
int pY = 265;
private RectF mRectangle = null;

public RectF getRectangle() {
return mRectangle;
}

Timer t = new Timer();

public Bloc() {
this.mRectangle = new RectF(pX, pY, pX + 25, pY + 25);
t.scheduleAtFixedRate(new Action(), 1000, 600);
}

class Action extends TimerTask {
public void run() {
pX--;
mRectangle.set(pX, pY, pX + 25, pY + 25); // Mettre à jour les coordonnées du rectangle
}
}
}
```

15 answers

scinarf Posted messages 1183 Status Member 294
 
Hello,

So I don't quite see what you want to do... a lot of strange things!

To get the time on Android, you need to use System.currentTimeMillis()

Then to trigger an event after a certain delay, you can do
long MyFutureAction = System.currentTimeMillis() + Delay;
while (System.currentTimeMillis() < MyFutureAction)
// Do action after the wait.

More info here: https://developer.android.com/reference/java/lang/System.html#currentTimeMillis%28%29
Official Android documentation, feel free to use it

Do you have to use Inner Classes (Classes defined directly in the same .java as another Class)? I find that it makes the code completely unreadable!

To move an object on the screen, like Drawable or Bitmap, I recommend using a Thread associated with a handler, which is much clearer and cleaner in my opinion.

If you have questions about how to do it, I invite you to ask them.
1
matrix124 Posted messages 136 Status Member 57
 
Hello,

First of all, thank you for your response.

As a complete beginner, I know my code is bad. However, I haven't really had the time to learn how to properly use Java since this is a project I have to present for my Baccalauréat as part of the Computer and Digital Sciences option. Moreover, I haven't received any training.

Regarding my application, in reality, I want to perform a certain action every 50ms, for example, rather than just knowing the time with currentTimeMillis. As for the Thread associated with a handler, I really have no idea how to set that up.

I would like to know how I can move blocks like in this mini-game (Line Runner): https://play.google.com/store/apps/details?id=com.djinnworks.linerunnerfree&hl=en

However, having no idea how to do it, I thought it would be good to use a Timer, but apparently that method doesn’t seem appropriate. If you could guide me towards a more suitable way to make blocks scroll across the screen like in the game, I would greatly appreciate it.

Thank you in advance.
0
scinarf Posted messages 1183 Status Member 294
 
Know that there is no problem with being a beginner ;) and you have to start somewhere.

I can see roughly what you want to do:

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);//10 pixel rectangle

}

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, "Error on thread sleep " + e);
}
}
}

public rectangle getRectangle()
{
return mRectangle;
}

}

This class allows creating a rectangle, once the deplaceRect method is called, it will repeat 50 times and will move the rectangle by the "step" used every "time" times

Then you need an object to draw, and in the deplaceRect method you can do Objetadessiner.setBounds(mRectangle);
Objetadessiner.invalidate();

The object will be drawn for 50 times. (you can change the 50 to another condition, like moving the Objetadessiner as long as it is not off-screen, or for a certain period of time, or if you are making a game, as long as the player has not lost, it all depends on your criteria)

Don't hesitate if you want more information.
0
matrix124 Posted messages 136 Status Member 57
 
Once again, I would like to thank you for the time you are giving me and your support.

I hope you don’t think that I haven’t thought this through or that I am asking the following questions out of laziness.

I have understood the entire code you provided me except for Log.d(TAG, "Error on thread sleep "+e); that’s why in the following code I commented it out because Eclipse was showing me an error and I had no idea how to fix it. As for the step and the time, I have currently set arbitrary values that I will modify later by observing the result.

It turns out that to create my mini-game, I used a structure from the Zero site where I only had to deal with the Draw method. Here is part of it:
public class MainSurfaceView extends SurfaceView implements SurfaceHolder.Callback { Bloc b = new Bloc(); @Override protected void onDraw(Canvas pCanvas) { // Draw here! 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); } 


However, you mentioned "Objetadessiner.setBounds(mRectangle); Objetadessiner.invalidate(); ", and I still don’t really understand what that refers to. If you could shed some light on this so that I understand how I will be able to display this moving Bloc.
Thank you.
0
scinarf Posted messages 1183 Status Member 294
 
No problem, I know it can seem quite obscure at first.

However, I don't always agree with the "Site du Zéro," I find it complicates the simplest things... like here, for example.

What shape do you wish to draw, a simple square for starters? Does that work for you?

(You may need to adapt the package name)
We'll proceed with two different .java files; it's much clearer:

The first one that contains the MainActivity and is named quite obviously MainActivity.java (when you create it)

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

}

The second one that contains my view (View) with a Drawable, an object that can be drawn on the screen
(you may need to change the package name again). This file is named myView.java
To test it on my HTC Desire, I modified the initial values of pX and 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); // Rectangle of 10 pixels
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, "Error on thread sleep " + e);
}
}
}

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

Invalidate is used to force the UI thread to redraw the objects on the screen. To use the deplaceRect method, simply place this line in the main activity after the setContentView(v)

v.deplaceRect(5, 15);

Time is in milliseconds, so you won't see much with 15, but feel free to test.
0
matrix124 Posted messages 136 Status Member 57
 
Thank you, however after several tests on my phone and my emulator, I only get an animation but just a blue square displayed on a black background.
That's why I was wondering if the code was working on your HTC Desire because if I increase the time in thread.sleep it just takes longer to display the result of the movement. This results in a white screen for several seconds followed by the display of the blue square on the black background.
I think I should let you be for today at least.
Thank you and have a good evening.
0
scinarf Posted messages 1183 Status Member 294
 
To start the animation, you need to add:

V.move(5,100) after the call to the setContentView(v) method.
0
matrix124 Posted messages 136 Status Member 57
 
Yes, it's good, she's already there and yet nothing is happening.
0
scinarf Posted messages 1183 Status Member 294
 
So the best thing is to trigger an activation on the click of a button, why not the Menu button. I will work on that.
0
matrix124 Posted messages 136 Status Member 57
 
Alright, I'm going to continue with some things on my application because I still have a lot of work left.
0
scinarf Posted messages 1183 Status Member 294
 
I will take a closer look at this this weekend if you can wait until then.
0
matrix124 Posted messages 136 Status Member 57
 
Of course, and have a good evening.
0
scinarf Posted messages 1183 Status Member 294
 
Hello, well rested? I'm coming back to you with a few questions.

What kind of interaction with the user do you want? A Start button to launch the animation, is that okay with you? Or just by touching the screen (anywhere)?
0
matrix124 Posted messages 136 Status Member 57
 
Good evening, touching the screen would be perfect.
0
scinarf Posted messages 1183 Status Member 294
 
Here is the translated text: Here, I did something that works and that is super neat!

Some conventions I use:

-1 Class = 1 Java file, so no Inner Class
-Each object has its destructor to avoid significant memory leaks (not managed by the GC)
-I commented the code so that each object is clear and each line is understandable.

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;//Retrieving the screen size to adapt the program to all sizes

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

//Action to be performed when a finger touches the screen
//Two actions identified, placing a finger and moving a finger
@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 to avoid memory leaks
@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";//Allows to identify the activity in the log
private ShapeDrawable Sd =null;//The object that is drawn

private int pX=100;//X Position at the program launch
private int pY=100;//Y Position at the program launch
private Rect mRectangle = null;//Rectangle used for the initial position
private Movement myAnimation=null;//Thread responsible for the animation of the 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);//Rectangle of 10 pixels
Sd.setBounds(mRectangle);
D = d;
myAnimation = new Movement(this, d);
}
public myView(Context context, AttributeSet attrs) //Unused constructor
{
super(context, attrs);
// TODO Auto-generated constructor stub
}

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

//Method called to draw (or update) the screen
@Override
public void onDraw(Canvas canvas)
{
Log.d(TAG, "Draw called"); //Log visible in LogCat
Sd.draw(canvas); //Redraw the ShapeDrawable
}

//Method called when pressing on the screen
@Override
public boolean onTouchEvent(MotionEvent event)
{
if (!myAnimation.getStatut()) myAnimation.start();//If the Thread is not launched then launch it
else if (myAnimation.getfinish())//If the Thread has been launched and if it is finished then recreate one
{
myAnimation.onDestroy();
myAnimation = null;
myAnimation = new Movement(this,D);
}
return true;

}

//Getter to retrieve the ShapeDrawable
public ShapeDrawable getShapeDrawable()
{
return Sd;
}

//Destructor automatically called to avoid having references or pointers remaining non-null when closing the 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();//Retrieve the Drawable from the view created in MainActivity
SD.setBounds(X_ini,Y_ini,X_ini + 20,Y_ini + 20);//For the needs of the Thread, reposition the ShapeDrawable on the right edge of the screen
Width = d.getWidth();
UIHandler = new sdhandler(v);
UIHandler.sendEmptyMessage(INVALIDATE);//Draw the ShapeDrawable for the first time
}

//Method that allows executing code in parallel (and not sequentially)
public void run()
{
Start = true;//Inform the program that the Thread is already launched to avoid launching it twice
while (SD.getBounds().right < Width)//Stopping criterion until we reach the right edge, can be changed
{
//A Thread cannot change the graphical interface, that's why we call a Handler that is executed on the main Thread
//So it is possible to change the graphical interface
UIHandler.sendEmptyMessage(INVALIDATE);//Communicating with the Handler by sending int type Messages
try {
Thread.sleep(100);//Wait 0.1 Seconds for the ShapeDrawable to be redrawn, may be modified for more responsiveness
} catch (InterruptedException e) {
// TODO Auto-generated catch block
Log.d(TAG,"Error Sleep " + e);//Retrieve error and interruption on Thread.sleep mandatory
break;
}
}
finish = true;
}

//Getter for the Thread Status
public boolean getStatut()
{
return Start;
}
//Getter to know if the Thread is finished
public boolean getfinish()
{
return finish;
}

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

Handler

package com.example.testshape;

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

//The Handler allows interaction with the elements of the Main Thread called UI Thread
public class sdhandler extends Handler
{

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

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

//Method allowing communication between the Thread and the 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 );//Move the ShapeDrawable
mv.invalidate();//Request to refresh the screen
break;
}
default :
break;
}
}

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

If you have any questions, no worries.
0
scinarf Posted messages 1183 Status Member 294
 
The names of Java files must be the same as the names of declared classes

Such as:
MainActivity
myView
movement
sdhandler
0
matrix124 Posted messages 136 Status Member 57
 
Beautiful, it works wonderfully! Thank you so much for everything you have done to help me, it's really great and it's exactly the result I was hoping to achieve.
It's really very well detailed thanks to the many comments you were able to leave. Thanks again.
0
scinarf Posted messages 1183 Status Member 294
 
After that, it's up to you to do the rest ;)

But don't hesitate to contact me again on this forum, I check in regularly. And from that, you can do quite a few things to have fun.

Remember to change the topic status to resolved ;)
0