Android Canvas进阶之自定义View实现粒子爆炸效果

话不多少,先上效果图,觉得简单的请轻喷。

老婆爆炸。。。哦不,是图片爆炸后是多个粒子移动,因此每个粒子都是一个对象,首先定义粒子对象,粒子是圆形的,首先是粒子的颜色,,然后是粒子的圆心坐标和粒子半径,这就已经能够确定一个粒子,然后爆炸后粒子需要有速度和加速度,上码

public class Ball {
    public int color; //图片像素点颜色值
    public float x; //粒子圆心坐标x
    public float y; //粒子圆心坐标y
    public float r; //粒子半径

    public float vX;//粒子运动水平方向速度
    public float vY;//粒子运动垂直方向速度
    public float aX;//粒子运动水平方向加速度
    public float aY;//粒子运动垂直方向加速度
}

接下来按照思路开始实现,首先定义SplitView继承自View,构造函数中初始化一只画笔,初始画一张要爆炸的BitMap,然后对BitMap进行采样,我们对粒子的半径定义为15px,那就是每隔2*15个像素,采样一个颜色值,然后整个粒子的颜色都按照采样到的颜色值,粒子的圆心坐标就是采样点的坐标,然后粒子的水平初始速度和竖直初始速度利用随机数随机产生-20到20之间,我们模拟的是真实爆炸,水平的加速度设置为0,竖直的加速度设置为6,当然你也可以给更大或者更小,定义一个Ball的list存住这些球的初始状态,上代码

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);
            }
        }
    }

好了,现在已经把所有粒子初始化好了,定义了一个开关布尔值isSplit来判断是否开始爆炸,然后开始绘制如果还没有开始爆炸前,我们把bitmap绘制出来,如果开始爆炸,我们则绘制粒子集合,不再绘制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);
            }
        }
    }

好了,现在一切都准备好了,还差一个触发条件,那就是什么时候爆炸,爆炸后粒子的坐标怎么发生变化,简单起见,在点击了view之后就开始爆炸吧

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

触发条件也有了,你以为现在你点了屏幕他会动吗,不,你还没有定义怎么动,所以现在点了之后球用没有,接下来看看怎么样更新坐标吧,我们首先需要将标志位isSplit改为true,然后,新建一个属性动画,让属性动画循环播放,我们循环的改变每个粒子的横坐标和纵坐标,然后调用invalidate方法,invalidate方法会调用view的ondraw方法,粒子重新绘制,就这样,粒子就动起来了

    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();
    }

完整的SplitView代码:


public class SplitView extends View {

    private static final String TAG = "SplitView";
    private Paint mPaint;
    private float r = 15f;//爆炸后粒子的半径
    private boolean isSplit = false;//是否开始爆炸
    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);
    }

}

附上Demo:https://github.com/987570437/PaintDemo

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android定义View中的Canvas是一个绘图容器,可以在其上行2D绘图操作。通过Canvas,我们可以绘制图形、文字、位图等。 要在自定义View中使用Canvas,需要重写View的onDraw()方法,并在该方法中获取Canvas实例,然后行绘制操作。 下面是一个简单的示例代码,展示如何在自定义View中使用Canvas绘制一个矩形: ```java public class MyCustomView extends View { public MyCustomView(Context context) { super(context); } public MyCustomView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); paint.setColor(Color.RED); paint.setStyle(Paint.Style.FILL); canvas.drawRect(100, 100, 300, 300, paint); } } ``` 在这个示例中,我们创建了一个名为MyCustomView的自定义View,并重写了它的onDraw()方法。在该方法中,我们首先创建了一个Paint对象,设置了画笔的颜色为红色,并指定绘制的样式为填充。然后,使用Canvas的drawRect()方法绘制一个矩形,坐标为(100, 100)到(300, 300)。 当我们在布局文件中使用这个自定义View时,它会自动调用onDraw()方法行绘制,从而在屏幕上显示出红色矩形。 需要注意的是,Canvas提供了许多其他绘制方法,如drawCircle()、drawText()等,可以根据需求选择合适的方法行绘制操作。此外,还可以通过设置Paint对象的属性来实现不同的绘制效果,如线条宽度、字体大小等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值