1

In my app I have a surface view with particle explosion animation.I was trying to make that surface view, transparent. After making it transparent, explode animation have a problem. I have attached screenshots of original animation and after making surface view transparent. When setting background color black, its working fine.

Original animation

After making surface view transparent

MainActivity.java

public class MainActivity extends Activity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LinearLayout surface = (LinearLayout)findViewById(R.id.middleSurface);
        surface.addView(new MainGamePanel(this));

        }
    }

MainGamePanel.java

 public class MainGamePanel extends SurfaceView implements
        SurfaceHolder.Callback {

    private static final String TAG = MainGamePanel.class.getSimpleName();

    private static final int EXPLOSION_SIZE = 200;

    private MainThread thread;
    private Explosion explosion;

    // the fps to be displayed
    private String avgFps;
    public void setAvgFps(String avgFps) {
        this.avgFps = avgFps;
    }

    public MainGamePanel(Context context) {
        super(context);
        // adding the callback (this) to the surface holder to intercept events
         this.setBackgroundColor(Color.TRANSPARENT); //To make canvas transparent                
          this.setZOrderOnTop(true); //To make canvas transparent                
          getHolder().setFormat(PixelFormat.TRANSPARENT); //To make canvas transparent!

        getHolder().addCallback(this);

        // make the GamePanel focusable so it can handle events
        setFocusable(true);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // create the game loop thread
        thread = new MainThread(getHolder(), this);

        // at this point the surface is created and
        // we can safely start the game loop
        thread.setRunning(true);
        thread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.d(TAG, "Surface is being destroyed");
        // tell the thread to shut down and wait for it to finish
        // this is a clean shutdown
        boolean retry = true;
        while (retry) {
            try {
                thread.setRunning(false);
                thread.join();
                retry = false;
            } catch (InterruptedException e) {
                // try again shutting down the thread
            }
        }
        Log.d(TAG, "Thread was shut down cleanly");
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            // handle touch
            // check if explosion is null or if it is still active
            if (explosion == null || explosion.getState() == Explosion.STATE_DEAD) {
                explosion = new Explosion(EXPLOSION_SIZE, (int)event.getX(), (int)event.getY());
            }
        }
        return true;
    }
    public void render(Canvas canvas) {

        canvas.drawColor(Color.argb(0, 0, 0, 0)); //To make canvas transparent

        // render explosions
        if (explosion != null) {
            explosion.draw(canvas);
        }

        // display fps
        //displayFps(canvas, avgFps);

        // display border
        /*Paint paint = new Paint();
        paint.setColor(Color.GREEN);
        canvas.drawLines(new float[]{
                0,0, canvas.getWidth()-1,0, 
                canvas.getWidth()-1,0, canvas.getWidth()-1,canvas.getHeight()-1, 
                canvas.getWidth()-1,canvas.getHeight()-1, 0,canvas.getHeight()-1,
                0,canvas.getHeight()-1, 0,0
        }, paint);*/
    }

    /**
     * This is the game update method. It iterates through all the objects
     * and calls their update method if they have one or calls specific
     * engine's update method.
     */
    public void update() {
        // update explosions
        if (explosion != null && explosion.isAlive()) {
            explosion.update(getHolder().getSurfaceFrame());
        }
    }

    private void displayFps(Canvas canvas, String fps) {
        if (canvas != null && fps != null) {
            Paint paint = new Paint();
            paint.setARGB(255, 255, 255, 255);
            canvas.drawText(fps, this.getWidth() - 50, 20, paint);
        }
    }

}

Explosion.java

    public class Explosion {

    private static final String TAG = Explosion.class.getSimpleName();

    public static final int STATE_ALIVE     = 0;    // at least 1 particle is alive
    public static final int STATE_DEAD      = 1;    // all particles are dead

    private Particle[] particles;           // particles in the explosion
    private int x, y;                       // the explosion's origin
    private float gravity;                  // the gravity of the explosion (+ upward, - down)
    private float wind;                     // speed of wind on horizontal
    private int size;                       // number of particles
    private int state;                      // whether it's still active or not

    public Explosion(int particleNr, int x, int y) {
        Log.d(TAG, "Explosion created at " + x + "," + y);
        this.state = STATE_ALIVE;
        this.particles = new Particle[particleNr];
        for (int i = 0; i < this.particles.length; i++) {
            Particle p = new Particle(x, y);
            this.particles[i] = p;
        }
        this.size = particleNr;
    }

    public Particle[] getParticles() {
        return particles;
    }
    public void setParticles(Particle[] particles) {
        this.particles = particles;
    }
    public int getX() {
        return x;
    }
    public void setX(int x) {
        this.x = x;
    }
    public int getY() {
        return y;
    }
    public void setY(int y) {
        this.y = y;
    }
    public float getGravity() {
        return gravity;
    }
    public void setGravity(float gravity) {
        this.gravity = gravity;
    }
    public float getWind() {
        return wind;
    }
    public void setWind(float wind) {
        this.wind = wind;
    }
    public int getSize() {
        return size;
    }
    public void setSize(int size) {
        this.size = size;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    // helper methods -------------------------
    public boolean isAlive() {
        return this.state == STATE_ALIVE;
    }
    public boolean isDead() {
        return this.state == STATE_DEAD;
    }

    public void update() {
        if (this.state != STATE_DEAD) {
            boolean isDead = true;
            for (int i = 0; i < this.particles.length; i++) {
                if (this.particles[i].isAlive()) {
                    this.particles[i].update();
                    isDead = false;
                }
            }
            if (isDead)
                this.state = STATE_DEAD; 
        }
    }

    public void update(Rect container) {
        if (this.state != STATE_DEAD) {
            boolean isDead = true;
            for (int i = 0; i < this.particles.length; i++) {
                if (this.particles[i].isAlive()) {
                    this.particles[i].update(container);
//                  this.particles[i].update();
                    isDead = false;
                }
            }
            if (isDead)
                this.state = STATE_DEAD; 
        }
    }

    public void draw(Canvas canvas) {
        for(int i = 0; i < this.particles.length; i++) {
            if (this.particles[i].isAlive()) {
                this.particles[i].draw(canvas);
            }
        }
    }
}

Particle.java

 public class Particle {

    public static final int STATE_ALIVE = 0;    // particle is alive
    public static final int STATE_DEAD = 1;     // particle is dead

    public static final int DEFAULT_LIFETIME    = 200;  // play with this
    public static final int MAX_DIMENSION       = 5;    // the maximum width or height
    public static final int MAX_SPEED           = 10;   // maximum speed (per update)

    private int state;          // particle is alive or dead
    private float widht;        // width of the particle
    private float height;       // height of the particle
    private float x, y;         // horizontal and vertical position
    private double xv, yv;      // vertical and horizontal velocity
    private int age;            // current age of the particle
    private int lifetime;       // particle dies when it reaches this value
    private int color;          // the color of the particle
    private Paint paint;        // internal use to avoid instantiation


    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public float getWidht() {
        return widht;
    }

    public void setWidht(float widht) {
        this.widht = widht;
    }

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    public double getXv() {
        return xv;
    }

    public void setXv(double xv) {
        this.xv = xv;
    }

    public double getYv() {
        return yv;
    }

    public void setYv(double yv) {
        this.yv = yv;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getLifetime() {
        return lifetime;
    }

    public void setLifetime(int lifetime) {
        this.lifetime = lifetime;
    }

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    // helper methods -------------------------
    public boolean isAlive() {
        return this.state == STATE_ALIVE;
    }
    public boolean isDead() {
        return this.state == STATE_DEAD;
    }

    public Particle(int x, int y) {
        this.x = x;
        this.y = y;
        this.state = Particle.STATE_ALIVE;
        this.widht = rndInt(1, MAX_DIMENSION);
        this.height = this.widht;
//      this.height = rnd(1, MAX_DIMENSION);
        this.lifetime = DEFAULT_LIFETIME;
        this.age = 0;
        this.xv = (rndDbl(0, MAX_SPEED * 2) - MAX_SPEED);
        this.yv = (rndDbl(0, MAX_SPEED * 2) - MAX_SPEED);
        // smoothing out the diagonal speed
        if (xv * xv + yv * yv > MAX_SPEED * MAX_SPEED) {
            xv *= 0.7;
            yv *= 0.7;
        }
        this.color = Color.argb(255, rndInt(0, 255), rndInt(0, 255), rndInt(0, 255));
        this.paint = new Paint(this.color);
    }

    /**
     * Resets the particle
     * @param x
     * @param y
     */
    public void reset(float x, float y) {
        this.state = Particle.STATE_ALIVE;
        this.x = x;
        this.y = y;
        this.age = 0;
    }

    // Return an integer that ranges from min inclusive to max inclusive.
    static int rndInt(int min, int max) {
        return (int) (min + Math.random() * (max - min + 1));
    }

    static double rndDbl(double min, double max) {
        return min + (max - min) * Math.random();
    }

    public void update() {
        if (this.state != STATE_DEAD) {
            this.x += this.xv;
            this.y += this.yv;

            // extract alpha
            int a = this.color >>> 24;
            a -= 2;                             // fade by 5
            if (a <= 0) {                       // if reached transparency kill the particle
                this.state = STATE_DEAD;
            } else {
                this.color = (this.color & 0x00ffffff) + (a << 24);     // set the new alpha
                this.paint.setAlpha(a);
                this.age++;                     // increase the age of the particle
//              this.widht *= 1.05;
//              this.height *= 1.05;
            }
            if (this.age >= this.lifetime) {    // reached the end if its life
                this.state = STATE_DEAD;
            }

            // http://lab.polygonal.de/2007/05/10/bitwise-gems-fast-integer-math/
            //32bit
//          var color:uint = 0xff336699;
//          var a:uint = color >>> 24;
//          var r:uint = color >>> 16 & 0xFF;
//          var g:uint = color >>>  8 & 0xFF;
//          var b:uint = color & 0xFF;

        }
    }

    public void update(Rect container) {
        // update with collision
        if (this.isAlive()) {
            if (this.x <= container.left || this.x >= container.right - this.widht) {
                this.xv *= -1;
            }
            // Bottom is 480 and top is 0 !!!
            if (this.y <= container.top || this.y >= container.bottom - this.height) {
                this.yv *= -1;
            }
        }
        update();
    }

    public void draw(Canvas canvas) {
//      paint.setARGB(255, 128, 255, 50);
        paint.setColor(this.color);
        canvas.drawRect(this.x, this.y, this.x + this.widht, this.y + this.height, paint);
//      canvas.drawCircle(x, y, widht, paint);
    }

}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"

android:background="#556168"
tools:context="com.offero.MainActivity$PlaceholderFragment" >



<ImageView
    android:id="@+id/imageView4"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop"
    android:src="@drawable/bg" />

<ImageView
    android:id="@+id/imageView2"
    android:layout_width="70dp"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:layout_marginRight="5dp"
    android:src="@drawable/cloud2" />

<ImageView
    android:id="@+id/imageView1"
    android:layout_width="70dp"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_marginLeft="5dp"
    android:src="@drawable/cloud1" />

<ImageView
    android:id="@+id/ImageView02"
    android:layout_width="70dp"
    android:layout_height="wrap_content"
    android:layout_alignLeft="@+id/imageView2"
    android:layout_alignParentBottom="true"
    android:layout_marginRight="5dp"
    android:src="@drawable/cloud2" />

<ImageView
    android:id="@+id/ImageView01"
    android:layout_width="70dp"
    android:layout_height="wrap_content"
    android:layout_alignLeft="@+id/imageView1"
    android:layout_alignParentBottom="true"
    android:layout_marginLeft="5dp"
    android:src="@drawable/cloud1" />

<ImageView
    android:id="@+id/ImageView03"
    android:layout_width="70dp"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:src="@drawable/cloud1" />

<ImageView
    android:id="@+id/imageView3"
    android:layout_width="wrap_content"
    android:layout_height="150dp"
    android:layout_centerInParent="true"
    android:src="@drawable/rocket" />

<ImageView
    android:id="@+id/imageView5"
    android:layout_width="wrap_content"
    android:layout_height="58dp"
    android:layout_alignTop="@+id/imageView3"
    android:layout_centerHorizontal="true"
    android:src="@drawable/rocketpeice2" />

<ImageView
    android:id="@+id/imageView6"
    android:layout_width="wrap_content"
    android:layout_height="60dp"
    android:layout_below="@+id/imageView5"
    android:layout_centerHorizontal="true"
    android:src="@drawable/rocketpeice1" />

<ImageView
    android:id="@+id/imageView_star1"
    android:layout_width="wrap_content"
    android:layout_height="10dp"
    android:layout_alignTop="@+id/imageView6"
    android:layout_centerHorizontal="true"
    android:src="@drawable/star" />

<ImageView
    android:id="@+id/imageView_star2"
    android:layout_width="wrap_content"
    android:layout_height="15dp"
    android:layout_alignTop="@+id/imageView6"
    android:layout_centerHorizontal="true"
    android:src="@drawable/star" />

<ImageView
    android:id="@+id/ImageView_star3"
    android:layout_width="wrap_content"
    android:layout_height="7dp"
    android:layout_alignTop="@+id/imageView6"
    android:layout_centerHorizontal="true"
    android:src="@drawable/star" />

<ImageView
    android:id="@+id/ImageView_star4"
    android:layout_width="wrap_content"
    android:layout_height="12dp"
    android:layout_alignBottom="@+id/ImageView_star3"
    android:layout_centerHorizontal="true"
    android:src="@drawable/star" />

<ImageView
    android:id="@+id/ImageView_star5"
    android:layout_width="wrap_content"
    android:layout_height="10dp"
    android:layout_alignLeft="@+id/imageView_star1"
    android:layout_below="@+id/imageView5"
    android:src="@drawable/star" />

<ImageView
    android:id="@+id/ImageView_star6"
    android:layout_width="wrap_content"
    android:layout_height="15dp"
    android:layout_alignLeft="@+id/imageView_star1"
    android:layout_alignTop="@+id/imageView6"
    android:src="@drawable/star" />

<ImageView
    android:id="@+id/ImageView_star7"
    android:layout_width="wrap_content"
    android:layout_height="7dp"
    android:layout_alignLeft="@+id/imageView_star1"
    android:layout_below="@+id/ImageView_star3"
    android:src="@drawable/star" />

<ImageView
    android:id="@+id/ImageView_star8"
    android:layout_width="wrap_content"
    android:layout_height="12dp"
    android:layout_alignLeft="@+id/imageView_star1"
    android:layout_alignTop="@+id/imageView6"
    android:src="@drawable/star" />

<LinearLayout
    android:id="@+id/middleSurface"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
</LinearLayout>

Loktar
  • 34,764
  • 7
  • 90
  • 104
Basim Sherif
  • 5,384
  • 7
  • 48
  • 90

1 Answers1

3

The below code draws a colour over the canvas.

canvas.drawColor(Color.argb(0, 0, 0, 0)); //To make canvas transparent

I assume you were drawing black over before. Which would draw black, then the new particle positions. Aka clearing the canvas between draws.

Now that you are drawing clear over the canvas between draws, you are literally doing nothing. As drawing nothing has no effect.

What you need to actually do is clear the canvas.

you need to use the code:

  canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

This draws Transparent, but also tells it not to merge with existing particles, but to actually clear them :) Ref: https://stackoverflow.com/a/10882301/940834

Community
  • 1
  • 1
IAmGroot
  • 13,760
  • 18
  • 84
  • 154