话不多少,先上效果图,觉得简单的请轻喷。
老婆爆炸。。。哦不,是图片爆炸后是多个粒子移动,因此每个粒子都是一个对象,首先定义粒子对象,粒子是圆形的,首先是粒子的颜色,,然后是粒子的圆心坐标和粒子半径,这就已经能够确定一个粒子,然后爆炸后粒子需要有速度和加速度,上码
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);
}
}