Multi-threaded animated balls / simple particles tutorial

Put problem concerning Views, Layouts and other XML-Resources (like AndroidManifest) here.

Multi-threaded animated balls / simple particles tutorial

Postby headchem » Fri Mar 06, 2009 5:40 pm

This is my first post, so forgive me if I'm jumping into the wrong forum for this. I've been programming Android for a few weeks and I have a simple application that animates the display of circles using threads.

This app works just fine, and I think the code is well-organized, but performance feels sluggish. Could an expert tell me if I'm going about this whole "multi-threaded 2D particle system" the right way? I embedded 5 questions that I had in the comments of my code.

main.xml:

Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1.  
  2. <?xml version="1.0" encoding="utf-8"?>
  3.  
  4. <edu.elon.cs.ScreenView xmlns:android="http://schemas.android.com/apk/res/android"
  5.  
  6.         android:orientation="vertical" android:layout_width="fill_parent"
  7.  
  8.         android:layout_height="fill_parent">
  9.  
  10. </edu.elon.cs.ScreenView>
Parsed in 0.001 seconds, using GeSHi 1.0.8.4


Draw2DCircles.java

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2. package edu.elon.cs;
  3.  
  4.  
  5.  
  6. import android.app.Activity;
  7.  
  8. import android.os.Bundle;
  9.  
  10.  
  11.  
  12. public class Draw2DCircles extends Activity {
  13.  
  14.        
  15.  
  16.     /** Called when the activity is first created. */
  17.  
  18.     @Override
  19.  
  20.     public void onCreate(Bundle savedInstanceState) {
  21.  
  22.         super.onCreate(savedInstanceState);
  23.  
  24.         setContentView(R.layout.main);
  25.  
  26.     }
  27.  
  28. }
Parsed in 0.031 seconds, using GeSHi 1.0.8.4



ScreenView.java

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2. package edu.elon.cs;
  3.  
  4.  
  5.  
  6. import android.content.Context;
  7.  
  8. import android.os.Handler;
  9.  
  10. import android.os.Message;
  11.  
  12. import android.util.AttributeSet;
  13.  
  14. import android.view.MotionEvent;
  15.  
  16. import android.widget.FrameLayout;
  17.  
  18.  
  19.  
  20. // extends FrameLayout so I can use addView(Ball)
  21.  
  22. public class ScreenView extends FrameLayout {
  23.  
  24.  
  25.  
  26.         // required constructor from extending FrameLayout
  27.  
  28.         public ScreenView(Context context) {
  29.  
  30.                 super(context);
  31.  
  32.         }
  33.  
  34.  
  35.  
  36.         // required constructor from extending FrameLayout
  37.  
  38.         public ScreenView(Context context, AttributeSet attrs) {
  39.  
  40.                 super(context, attrs);
  41.  
  42.         }
  43.  
  44.  
  45.  
  46.         // if you touch anywhere on the screen, a Ball is drawn
  47.  
  48.         public boolean onTouchEvent(MotionEvent event) {
  49.  
  50.                 if (event.getAction() == MotionEvent.ACTION_DOWN) {
  51.  
  52.                         addBall();
  53.  
  54.                 }
  55.  
  56.                 return false; // QUESTION #1: does it matter if this is true or false?
  57.  
  58.         }
  59.  
  60.  
  61.  
  62.         // method to actually draw the ball
  63.  
  64.         public void addBall() {
  65.  
  66.                 // Ball needs a reference to this context, so I pass it in constructor
  67.  
  68.                 Ball newBall = new Ball(getContext());
  69.  
  70.                 // addView(View) is a built-in method in FrameLayout
  71.  
  72.                 // it automatically calls onDraw(Canvas) in the Ball object
  73.  
  74.                 // Ball extends View, which is where onDraw comes from
  75.  
  76.                 addView(newBall);
  77.  
  78.                 // create a new thread to manager the animation part of the ball
  79.  
  80.                 // Pass in refs to the individual ball, thread handler, this ScreenView
  81.  
  82.                 BallThread thread = new BallThread(newBall, handler, this);
  83.  
  84.                 // start the thread, which starts the animation
  85.  
  86.                 thread.start();
  87.  
  88.         }
  89.  
  90.  
  91.  
  92.         // define the handler that gets triggered when the Thread says so
  93.  
  94.         private Handler handler = new Handler() {
  95.  
  96.                 @Override
  97.  
  98.                 public void handleMessage(Message msg) {
  99.  
  100.                         // get the Ball obj that I put in the Message when I triggered it
  101.  
  102.                         Ball myBall = (Ball) msg.obj;
  103.  
  104.                         // invalidate() redraws the ball, this is what redraws the pixels
  105.  
  106.                         myBall.invalidate();
  107.  
  108.                 }
  109.  
  110.         };
  111.  
  112. }
  113.  
  114.  
Parsed in 0.037 seconds, using GeSHi 1.0.8.4


BallThread.java

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.  
  2. package edu.elon.cs;
  3.  
  4.  
  5.  
  6. import android.os.Handler;
  7.  
  8. import android.os.Message;
  9.  
  10.  
  11.  
  12. public class BallThread extends Thread {
  13.  
  14.  
  15.  
  16.         private Ball ball;
  17.  
  18.         private long startLife = 0;
  19.  
  20.         private Handler handler;
  21.  
  22.         private ScreenView sv;
  23.  
  24.  
  25.  
  26.         public BallThread(Ball ball, Handler handler, ScreenView sv) {
  27.  
  28.                 this.ball = ball;
  29.  
  30.                 this.handler = handler;
  31.  
  32.                 this.sv = sv;
  33.  
  34.                 // so I can know a start life time in ms
  35.  
  36.                 // QUESTION #2: how accurate are these milliseconds?
  37.  
  38.                 startLife = System.currentTimeMillis();
  39.  
  40.         }
  41.  
  42.  
  43.  
  44.         public void run() {
  45.  
  46.                 while (!ball.isDead()) {
  47.  
  48.                         // calculate how many ms this ball has been alive
  49.  
  50.                         long life = System.currentTimeMillis() - startLife;
  51.  
  52.  
  53.  
  54.                         // expand radius only in the initial 350 ms of life
  55.  
  56.                         // don't worry about the logic, it's just my particular animation
  57.  
  58.                         if (life < 350 && ball.getRadius() < ball.getMaxRadius()) {
  59.  
  60.                                 ball.setRadius((ball.getMaxRadius() / 6) + ball.getRadius());
  61.  
  62.                         }
  63.  
  64.  
  65.  
  66.                         // start fade out only after obj has lived 3 seconds
  67.  
  68.                         if (life > 3000) {
  69.  
  70.                                 ball.setAlpha(ball.getAlpha() - <img src="http://www.anddev.org/images/smilies/cool.png" alt="8)" title="Cool" />;
  71.  
  72.                         }
  73.  
  74.  
  75.  
  76.                         // clean up memory by removing this ball when it's flagged as dead
  77.  
  78.                         if (ball.isDead()) {
  79.  
  80.                                 sv.removeView(ball);
  81.  
  82.                                 // now exit out of the thread run() loop
  83.  
  84.                                 return;
  85.  
  86.                         }
  87.  
  88.  
  89.  
  90.                         try {
  91.  
  92.                                 // Message telling everybody the ball properties have changed
  93.  
  94.                                 Message myMessage = new Message();
  95.  
  96.                                 // stick a ref of the ball in the Message for retrival by others
  97.  
  98.                                 myMessage.obj = ball;
  99.  
  100.                                 // fire off the Message
  101.  
  102.                                 handler.sendMessage(myMessage);
  103.  
  104.  
  105.  
  106.                                 sleep(1000 / 30); // QUESTION #3: Is this where I set 30 FPS?
  107.  
  108.                         } catch (InterruptedException e) {
  109.  
  110.                                 e.printStackTrace();
  111.  
  112.                         }
  113.  
  114.                 }
  115.  
  116.         }
  117.  
  118. }
  119.  
  120.  
Parsed in 0.041 seconds, using GeSHi 1.0.8.4


Ball.java

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package edu.elon.cs;
  2.  
  3.  
  4.  
  5. import java.util.Random;
  6.  
  7.  
  8.  
  9. import android.content.Context;
  10.  
  11. import android.graphics.Canvas;
  12.  
  13. import android.graphics.Paint;
  14.  
  15. import android.view.View;
  16.  
  17.  
  18.  
  19. // extends View so I can have onDraw(Canvas) take care of things
  20.  
  21. public class Ball extends View {
  22.  
  23.  
  24.  
  25.         private int x = 0;
  26.  
  27.         private int y = 0;
  28.  
  29.         private int alpha = 0;
  30.  
  31.         private int red = 255;
  32.  
  33.         private int green = 255;
  34.  
  35.         private int blue = 255;
  36.  
  37.         private int radius = 0;
  38.  
  39.         private int maxRadius = 0;
  40.  
  41.  
  42.  
  43.         // this is the important one, you could change all the others
  44.  
  45.         boolean isDead = false;
  46.  
  47.  
  48.  
  49.         // logic in constructor randomizes color, size, alpha, and coordinates
  50.  
  51.         public Ball(Context context) {
  52.  
  53.                 super(context);
  54.  
  55.  
  56.  
  57.                 Random rand = new Random();
  58.  
  59.                 // QUESTION #4: how can I get screen height and width from Context?
  60.  
  61.                 x = rand.nextInt(260) + 1;
  62.  
  63.                 y = rand.nextInt(360) + 1;
  64.  
  65.                 alpha = rand.nextInt(255) + 1;
  66.  
  67.                 maxRadius = rand.nextInt(80) + 1;
  68.  
  69.  
  70.  
  71.                 if (alpha < 50) {
  72.  
  73.                         alpha += 50;
  74.  
  75.                 }
  76.  
  77.  
  78.  
  79.                 red = rand.nextInt(255) + 1;
  80.  
  81.                 green = rand.nextInt(255) + 1;
  82.  
  83.                 blue = rand.nextInt(255) + 1;
  84.  
  85.         }
  86.  
  87.  
  88.  
  89.         // if alpha is less than 0, flag this as dead so the thread will remove it
  90.  
  91.         @Override
  92.  
  93.         protected void onDraw(Canvas canvas) {
  94.  
  95.                 if (alpha <= 0) {
  96.  
  97.                         isDead = true;
  98.  
  99.                 } else {
  100.  
  101.                         Paint background = new Paint();
  102.  
  103.  
  104.  
  105.                         // QUESTION #5: how big is the performance hit for anti aliasing?
  106.  
  107.                         background.setAntiAlias(true);
  108.  
  109.                         background.setARGB(alpha, red, green, blue);
  110.  
  111.                         canvas.drawCircle(x, y, radius, background);
  112.  
  113.                 }
  114.  
  115.         }
  116.  
  117.  
  118.  
  119.         // getters and setters for my instance vars
  120.  
  121.  
  122.  
  123.         public int getAlpha() {
  124.  
  125.                 return alpha;
  126.  
  127.         }
  128.  
  129.  
  130.  
  131.         public void setAlpha(int alpha) {
  132.  
  133.                 this.alpha = alpha;
  134.  
  135.         }
  136.  
  137.  
  138.  
  139.         public int getMaxRadius() {
  140.  
  141.                 return maxRadius;
  142.  
  143.         }
  144.  
  145.  
  146.  
  147.         public int getRadius() {
  148.  
  149.                 return radius;
  150.  
  151.         }
  152.  
  153.  
  154.  
  155.         public void setRadius(int radius) {
  156.  
  157.                 this.radius = radius;
  158.  
  159.         }
  160.  
  161.  
  162.  
  163.         public boolean isDead() {
  164.  
  165.                 return isDead;
  166.  
  167.         }
  168.  
  169. }
  170.  
  171.  
Parsed in 0.044 seconds, using GeSHi 1.0.8.4


Attached is a .zip containing the complete Eclipse project. Thanks for your help!
Attachments
Draw2DCircles.zip
This is the complete Draw2DCircles Eclipse project
(34.32 KiB) Downloaded 48 times
headchem
Freshman
Freshman
 
Posts: 2
Joined: Fri Mar 06, 2009 4:49 pm

Top

Postby MrSnowflake » Sat Mar 07, 2009 3:38 pm

headchem wrote:This is my first post, so forgive me if I'm jumping into the wrong forum for this.
Why yes you are. I'm sure you realize you have a problem and not a tutorial to offer, so why post this in a tutorial forum? * MOVED *
User avatar
MrSnowflake
Moderator
Moderator
 
Posts: 1439
Joined: Sat Feb 16, 2008 3:11 pm
Location: Flanders, Belgium

Postby headchem » Sat Mar 07, 2009 8:08 pm

My post shows step by step how to draw animated circles on the screen, which is why I posted it as a tutorial. However, I also have questions about improving the code I posted in the tutorial.
headchem
Freshman
Freshman
 
Posts: 2
Joined: Fri Mar 06, 2009 4:49 pm

Postby MrSnowflake » Sat Mar 07, 2009 11:35 pm

I didn't look past your question. Still, I don't believe this should be on the tutorials, as they should be working and correct :). Though if you find the sollution, please repost this on the tutorials board!
User avatar
MrSnowflake
Moderator
Moderator
 
Posts: 1439
Joined: Sat Feb 16, 2008 3:11 pm
Location: Flanders, Belgium

Top

Return to View, Layout & Resource Problems

Who is online

Users browsing this forum: No registered users and 6 guests