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:
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <edu.elon.cs.ScreenView xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical" android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- </edu.elon.cs.ScreenView>
Parsed in 0.001 seconds, using GeSHi 1.0.8.4
Draw2DCircles.java
Using java Syntax Highlighting
- package edu.elon.cs;
- import android.app.Activity;
- import android.os.Bundle;
- public class Draw2DCircles extends Activity {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- }
- }
Parsed in 0.031 seconds, using GeSHi 1.0.8.4
ScreenView.java
Using java Syntax Highlighting
- package edu.elon.cs;
- import android.content.Context;
- import android.os.Handler;
- import android.os.Message;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.widget.FrameLayout;
- // extends FrameLayout so I can use addView(Ball)
- public class ScreenView extends FrameLayout {
- // required constructor from extending FrameLayout
- public ScreenView(Context context) {
- super(context);
- }
- // required constructor from extending FrameLayout
- public ScreenView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- // if you touch anywhere on the screen, a Ball is drawn
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- addBall();
- }
- return false; // QUESTION #1: does it matter if this is true or false?
- }
- // method to actually draw the ball
- public void addBall() {
- // Ball needs a reference to this context, so I pass it in constructor
- Ball newBall = new Ball(getContext());
- // addView(View) is a built-in method in FrameLayout
- // it automatically calls onDraw(Canvas) in the Ball object
- // Ball extends View, which is where onDraw comes from
- addView(newBall);
- // create a new thread to manager the animation part of the ball
- // Pass in refs to the individual ball, thread handler, this ScreenView
- BallThread thread = new BallThread(newBall, handler, this);
- // start the thread, which starts the animation
- thread.start();
- }
- // define the handler that gets triggered when the Thread says so
- private Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // get the Ball obj that I put in the Message when I triggered it
- Ball myBall = (Ball) msg.obj;
- // invalidate() redraws the ball, this is what redraws the pixels
- myBall.invalidate();
- }
- };
- }
Parsed in 0.035 seconds, using GeSHi 1.0.8.4
BallThread.java
Using java Syntax Highlighting
- package edu.elon.cs;
- import android.os.Handler;
- import android.os.Message;
- public class BallThread extends Thread {
- private Ball ball;
- private long startLife = 0;
- private Handler handler;
- private ScreenView sv;
- public BallThread(Ball ball, Handler handler, ScreenView sv) {
- this.ball = ball;
- this.handler = handler;
- this.sv = sv;
- // so I can know a start life time in ms
- // QUESTION #2: how accurate are these milliseconds?
- startLife = System.currentTimeMillis();
- }
- public void run() {
- while (!ball.isDead()) {
- // calculate how many ms this ball has been alive
- long life = System.currentTimeMillis() - startLife;
- // expand radius only in the initial 350 ms of life
- // don't worry about the logic, it's just my particular animation
- if (life < 350 && ball.getRadius() < ball.getMaxRadius()) {
- ball.setRadius((ball.getMaxRadius() / 6) + ball.getRadius());
- }
- // start fade out only after obj has lived 3 seconds
- if (life > 3000) {
- ball.setAlpha(ball.getAlpha() - <img src="http://www.anddev.org/images/smilies/cool.png" alt="8)" title="Cool" />;
- }
- // clean up memory by removing this ball when it's flagged as dead
- if (ball.isDead()) {
- sv.removeView(ball);
- // now exit out of the thread run() loop
- return;
- }
- try {
- // Message telling everybody the ball properties have changed
- Message myMessage = new Message();
- // stick a ref of the ball in the Message for retrival by others
- myMessage.obj = ball;
- // fire off the Message
- handler.sendMessage(myMessage);
- sleep(1000 / 30); // QUESTION #3: Is this where I set 30 FPS?
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
Parsed in 0.039 seconds, using GeSHi 1.0.8.4
Ball.java
Using java Syntax Highlighting
- package edu.elon.cs;
- import java.util.Random;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.view.View;
- // extends View so I can have onDraw(Canvas) take care of things
- public class Ball extends View {
- private int x = 0;
- private int y = 0;
- private int alpha = 0;
- private int red = 255;
- private int green = 255;
- private int blue = 255;
- private int radius = 0;
- private int maxRadius = 0;
- // this is the important one, you could change all the others
- boolean isDead = false;
- // logic in constructor randomizes color, size, alpha, and coordinates
- public Ball(Context context) {
- super(context);
- Random rand = new Random();
- // QUESTION #4: how can I get screen height and width from Context?
- x = rand.nextInt(260) + 1;
- y = rand.nextInt(360) + 1;
- alpha = rand.nextInt(255) + 1;
- maxRadius = rand.nextInt(80) + 1;
- if (alpha < 50) {
- alpha += 50;
- }
- red = rand.nextInt(255) + 1;
- green = rand.nextInt(255) + 1;
- blue = rand.nextInt(255) + 1;
- }
- // if alpha is less than 0, flag this as dead so the thread will remove it
- @Override
- protected void onDraw(Canvas canvas) {
- if (alpha <= 0) {
- isDead = true;
- } else {
- Paint background = new Paint();
- // QUESTION #5: how big is the performance hit for anti aliasing?
- background.setAntiAlias(true);
- background.setARGB(alpha, red, green, blue);
- canvas.drawCircle(x, y, radius, background);
- }
- }
- // getters and setters for my instance vars
- public int getAlpha() {
- return alpha;
- }
- public void setAlpha(int alpha) {
- this.alpha = alpha;
- }
- public int getMaxRadius() {
- return maxRadius;
- }
- public int getRadius() {
- return radius;
- }
- public void setRadius(int radius) {
- this.radius = radius;
- }
- public boolean isDead() {
- return isDead;
- }
- }
Parsed in 0.041 seconds, using GeSHi 1.0.8.4
Attached is a .zip containing the complete Eclipse project. Thanks for your help!


. Though if you find the sollution, please repost this on the tutorials board!