Advanced Custom View of Android Canvas to Implement Particle Explosion Effect

Keywords: Attribute github

Not much, first on the effect map, feel simple, please spray lightly.

My wife exploded... Oh no, there are many particles moving after the image explosion, so each particle is an object. First, the particle is defined as a particle object. The particle is circular, first is the color of the particle, then is the center coordinate and radius of the particle, which has been able to determine a particle. Then the particle needs to be accelerated and accelerated after the explosion. Speed, top code

public class Ball {
    public int color; //Pixel Pixel Color Value
    public float x; //Particle center coordinate x
    public float y; //Particle center coordinate y
    public float r; //Particle radius

    public float vX;//Horizontal velocity of particle motion
    public float vY;//Vertical velocity of particle motion
    public float aX;//Horizontal acceleration of particle motion
    public float aY;//Vertical acceleration of particle motion
}

Then we start to implement the idea. First, we define SplitView to inherit from View, initialize a brush in the constructor, initially draw a BitMap to explode, and then sample the BitMap. We define the radius of the particle as 15px, that is, every 2*15 pixels, sample a color value, and then the whole particle's face. The color is sampled according to the color value. The center coordinate of the particle is the coordinate of the sampling point. Then the initial horizontal and vertical velocities of the particle are randomly generated by random numbers from - 20 to 20. We simulate the real explosion. The horizontal acceleration is set to 0, and the vertical acceleration is set to 6. Of course, you can also give it to the real explosion. Larger or smaller, define a list of Ball s to hold the initial state of the balls, and code it.

private void init() {

        mPaint = new Paint();
        balls = new ArrayList<>();
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.girl);
        int width = mBitmap.getWidth();
        int height = mBitmap.getHeight();
        Random random = new Random();

        for (int i = 0; i < width; i += 2 * r) {
            for (int j = 0; j < height; j += 2 * r) {
                Ball ball = new Ball();
                ball.color = mBitmap.getPixel(i, j);
                ball.x = i;
                ball.y = j;
                ball.r = r;
                ball.vX = random.nextInt(39) + random.nextFloat() - 20.0f;//(-20 , 20)
                ball.vY = random.nextInt(39) + random.nextFloat() - 20.0f;//(-20 , 20)
                ball.aX = 0;
                ball.aY = 6f;
                balls.add(ball);
            }
        }
    }

Now that all the particles have been initialized, a switch Boolean value isSplit is defined to determine whether the explosion started. Then we start drawing bitmap before the explosion started. If the explosion started, we draw the particle set instead of drawing bitmap.

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        if (!isSplit) {
            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
        } else {
            for (Ball ball : balls) {
                mPaint.setColor(ball.color);
                canvas.drawCircle(ball.x, ball.y, ball.r, mPaint);
            }
        }
    }

Well, now that everything is ready, there's still one trigger missing. That's when the explosion happens and how the coordinates of the particles change after the explosion. For simplicity, start exploding after clicking on view.

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            updateBall();
        }
        return super.onTouchEvent(event);
    }

The trigger condition is also there. Do you think that if you click on the screen now, he will move? No, you haven't defined how to move. So if you click on the screen now, the ball will not be used. Next, let's see how to update the coordinates. First, we need to change the marker isSplit to true. Then, we need to create a new attribute animation to let the attribute animation play circularly. We change the abscissa and ordinate of each particle in a circular way, then call invalidate method, invalidate method calls ondraw method of view, the particle is redrawn, so the particle moves.

    private void updateBall() {
        isSplit = true;
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setDuration(30);
        animator.setRepeatCount(-1);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                for (Ball ball : balls) {
                    ball.x += ball.vX;
                    ball.y += ball.vY;
                    ball.vX += ball.aX;
                    ball.vY += ball.aY;
                    Log.d(TAG, "onAnimationUpdate: ");
                }
                invalidate();
            }
        });
        animator.start();
    }

Complete SplitView code:

public class SplitView extends View {

    private static final String TAG = "SplitView";
    private Paint mPaint;
    private float r = 15f;//Radius of particles after explosion
    private boolean isSplit = false;//Does it start to explode?
    List<Ball> balls;
    private Bitmap mBitmap;

    public SplitView(Context context) {
        this(context, null);
    }

    public SplitView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SplitView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {

        mPaint = new Paint();
        balls = new ArrayList<>();
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.girl);
        int width = mBitmap.getWidth();
        int height = mBitmap.getHeight();
        Random random = new Random();

        for (int i = 0; i < width; i += 2 * r) {
            for (int j = 0; j < height; j += 2 * r) {
                Ball ball = new Ball();
                ball.color = mBitmap.getPixel(i, j);
                ball.x = i;
                ball.y = j;
                ball.r = r;
                ball.vX = random.nextInt(39) + random.nextFloat() - 20.0f;//(-20 , 20)
                ball.vY = random.nextInt(39) + random.nextFloat() - 20.0f;//(-20 , 20)
                ball.aX = 0;
                ball.aY = 6f;
                balls.add(ball);
            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (!isSplit) {
            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
        } else {
            for (Ball ball : balls) {
                mPaint.setColor(ball.color);
                canvas.drawCircle(ball.x, ball.y, ball.r, mPaint);
            }
        }
    }

    private void updateBall() {
        isSplit = true;
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setDuration(30);
        animator.setRepeatCount(-1);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                for (Ball ball : balls) {
                    ball.x += ball.vX;
                    ball.y += ball.vY;
                    ball.vX += ball.aX;
                    ball.vY += ball.aY;
                    Log.d(TAG, "onAnimationUpdate: ");
                }
                invalidate();
            }
        });
        animator.start();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            updateBall();
        }
        return super.onTouchEvent(event);
    }

}

Attached Demo: https://github.com/987570437/PaintDemo

Posted by newburcj on Sat, 07 Sep 2019 04:50:16 -0700