Using Timer on Android
Solved
matrix124
Posted messages
136
Status
Member
-
scinarf Posted messages 1183 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
}
}
}
```
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
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.
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.
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.
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.
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.
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.
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:
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.
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.
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.
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.
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.
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.
To start the animation, you need to add:
V.move(5,100) after the call to the setContentView(v) method.
V.move(5,100) after the call to the setContentView(v) method.
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.
Alright, I'm going to continue with some things on my application because I still have a lot of work left.
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)?
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)?
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.
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.