一、Canvas详解
基础概念:画布,通过画笔绘制几何图形、文本、路径和位图等
常用API类型:常用API分为绘制、变换、状态保存和恢复
1.3、状态保存和恢复
调用save函数,压栈;restore函数,出栈。
二、粒子特效
首先来看效果:
分析:基本思路就是把这张图转成粒子,然后实现它的爆炸效果,就是将转换后的粒子进行位置的移动,类似于自由落体运动。
首先我们肯定是要准备一张图片,然后先来看下面这几行代码:
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);
}
}
到这里,我们的这个小案例也就介绍完了。