【Android】Canvas画布高级应用

一、Canvas详解

基础概念:画布,通过画笔绘制几何图形、文本、路径和位图等

常用API类型:常用API分为绘制、变换、状态保存和恢复
在这里插入图片描述
在这里插入图片描述

1.3、状态保存和恢复在这里插入图片描述

调用save函数,压栈;restore函数,出栈。

二、粒子特效

首先来看效果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201121154451630.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwODg1ODIx,size_16,color_FFFFFF,t_70#pic_cente
分析:基本思路就是把这张图转成粒子,然后实现它的爆炸效果,就是将转换后的粒子进行位置的移动,类似于自由落体运动。

首先我们肯定是要准备一张图片,然后先来看下面这几行代码:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ying); //解析得到一张Bitmap对象
bitmap.getWidth(); //宽,水平方向有多个像素点
bitmap.getHeight(); //高,垂直方向有多少个像素点
int pixel = bitmap.getPixel(0, 0); //此方法返回的是图片在当前位置像素的颜色值,入参是表示水平方向和垂直方向像素点的位置

在代码中我们肯定要首先获取到Bitmap对象,然后可以获取Bitmap对象的宽和高,也就是Bitmap在水平方向和垂直方向上的像素点的个数,最后我们可以通过bitmap的getPixel(x,y)方法来获取图片在当前位置像素点的颜色值,入参x,y表示水平方向和垂直方向像素点的位置,这样我们就可以获取每一张图片每一个像素点的颜色值,所以接下来我们可以把每一个像素点封装成一个粒子对象。

我们给粒子对象定义如下几个属性:

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;//粒子运动垂直方向加速度
 
}

OK,接着我们就可以来定义粒子爆炸的View了,首先来初始化画笔,Bitmap,粒子的直径,还有粒子集合,因为我们是把这张Bitmap给分解了,所以最后绘制在屏幕上的不是一张图像了,而是由一个个的小圆组成的:

private Paint mPaint;
private Bitmap mBitmap;
private float d = 3;//粒子直径
private ValueAnimator mAnimator;
private List<Ball> mBalls = new ArrayList<>();

接着遍历Bitmap的宽和高,获取每个位置上的颜色值,并且初始化粒子对象,为其设置相应的属性比如圆心坐标、半径、速度和加速度等等,将构建完成的粒子对象添加到粒子集合中:

setLayerType(View.LAYER_TYPE_SOFTWARE, null); //关闭硬件加速
    mPaint = new Paint();
    mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.liying);
    for (int i = 0; i < mBitmap.getWidth(); i++) {
        for (int j = 0; j < mBitmap.getHeight(); j++) {
            Ball ball = new Ball();
            ball.color = mBitmap.getPixel(i,j);
            ball.x = i * d + d/ 2;
            ball.y = j * d + d/ 2;
            ball.r = d / 2;
 
            //速度(-20,20)
            ball.vX = (float) (Math.pow(-1, Math.ceil(Math.random() * 1000)) * 20 * Math.random());
            ball.vY = rangInt(-15, 35);
            //加速度
            ball.aX = 0;
            ball.aY = 0.98f;
 
            mBalls.add(ball);
        }
    }    

接着初始化属性动画,为其设置执行时间、插值器以及监听执行的回调,在回调监听中刷新粒子的位置:

mAnimator = ValueAnimator.ofFloat(0,1);
        mAnimator.setRepeatCount(-1);
        mAnimator.setDuration(2000);
        mAnimator.setInterpolator(new LinearInterpolator());
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                updateBall();
                invalidate();
            }
        });

在点击这个View的时候执行动画,也就是需要在onTouchEvent的ACTION_DOWN事件中开启执行动画的操作:

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN){
            //执行动画
            mAnimator.start();
        }
        return super.onTouchEvent(event);
    }

完整代码如下:

public class SplitView extends View {  
 
    private Paint mPaint;
    private Bitmap mBitmap;
    private float d = 3;//粒子直径
    private ValueAnimator mAnimator; 
    private List<Ball> mBalls = new ArrayList<>();
 
    public SplitView(Context context) {
        this(context, null);
    }
 
    public SplitView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
 
    public SplitView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
 
    private void init(){
        setLayerType(View.LAYER_TYPE_SOFTWARE, null); //关闭硬件加速
        mPaint = new Paint();
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.liying);
        for (int i = 0; i < mBitmap.getWidth(); i++) {
            for (int j = 0; j < mBitmap.getHeight(); j++) {
                Ball ball = new Ball();
                ball.color = mBitmap.getPixel(i,j);
                ball.x = i * d + d/ 2;
                ball.y = j * d + d/ 2;
                ball.r = d / 2;
 
                //速度(-20,20)
                ball.vX = (float) (Math.pow(-1, Math.ceil(Math.random() * 1000)) * 20 * Math.random());
                ball.vY = rangInt(-15, 35);
                //加速度
                ball.aX = 0;
                ball.aY = 0.98f;
 
                mBalls.add(ball);
            }
        }
 
        mAnimator = ValueAnimator.ofFloat(0,1);
        mAnimator.setRepeatCount(-1);
        mAnimator.setDuration(2000);
        mAnimator.setInterpolator(new LinearInterpolator());
        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                updateBall();
                invalidate();
            }
        });
    }
 
    private int rangInt(int i, int j) {
        int max = Math.max(i, j);
        int min = Math.min(i, j) - 1;
        //在0到(max - min)范围内变化,取大于x的最小整数 再随机
        return (int) (min + Math.ceil(Math.random() * (max - min)));
    }
 
    private void updateBall() {
        //更新粒子的位置
        for (Ball ball : mBalls) {
            ball.x += ball.vX;
            ball.y += ball.vY;
            ball.vX += ball.aX;
            ball.vY += ball.aY;
        }
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(350,100);
        for (Ball ball : mBalls) {
            mPaint.setColor(ball.color);
            canvas.drawCircle(ball.x, ball.y, ball.r, mPaint);
        }
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN){
            //执行动画
            mAnimator.start();
        }
        return super.onTouchEvent(event);
    }
}

到这里,我们的这个小案例也就介绍完了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值