烟花效果搜出来好像很多,但是没有太好的例子,于是自己做了一个效果,仅仅是完成了定制的需求。看下效果:
效果是,一个气泡抛物线出来,然后爆开。大致跟上次分享的点赞特效差不多,仅仅是根据需求绘制不同的效果:SurfaceView实现点赞效果
把相同代码去掉了,要直接用的话拼接一下吧,这里主要是说一下绘制过程
/** * 直播页面点赞特效,采用SurfaceView绘制 * 与普通控件使用方法类似,点赞是只需要调用startBomb()即可 */
public class BombView extends SurfaceView implements SurfaceHolder.Callback {
private static final int MAX_BUBBLE_COUNT = 90;
private SurfaceHolder mHolder;
private DrawTask mDrawTask; // 绘制UI的线程
private Paint mPaint; // 绘制需要使用的画刷
private FuseView mFuseView; // 引导的view
private boolean mIsDismiss = false; //是否处于消失阶段
private int mWidth; // 控件的宽度
private int mHeight; // 控件的高度
private int mBombX; // 旋转的中心X
private int mBombY; // 旋转的中心Y
private int dx = 0; // 引导view在坐抛物线运动时在x轴的增量
private Bitmap[] mDrawables; // 存放需要展示的图
private int[] mDrawableResIDs; // 存放需要展示的图
private List<Bubble> mBubbles = Collections.synchronizedList(new LinkedList<Bubble>()); // 用于存放点赞信息
private Random mRandom = new Random(); // 用于产生随机数
public BombView(Context context, AttributeSet attrs) {
super(context, attrs);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setKeepScreenOn(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);
mPaint = new Paint();
mDrawableResIDs = new int[] {
R.drawable.praise_eight,
R.drawable.praise_one,
R.drawable.praise_third,
R.drawable.praise_two,
R.drawable.praise_five,
R.drawable.praise_four,
R.drawable.praise_seven,
R.drawable.praise_six,
};
mDrawables = new Bitmap[mDrawableResIDs.length];
}
@Override
protected void onDraw(final Canvas canvas) {
if (canvas != null) {
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); // 清空界面
if (mFuseView == null) {
return;
}
if (mFuseView.y+mFuseView.bitmap.getHeight()/2 > mBombY) {
mPaint.setAlpha(255);
if (mFuseView.x+mFuseView.bitmap.getWidth()/2 > mWidth/2) { // 抛物线
mFuseView.scale = dx*1.5f/mWidth + 1f;
canvas.save();
mFuseView.x = mWidth*7/8 - dx;
mFuseView.y = (int) getY(dx);
canvas.scale(mFuseView.scale, mFuseView.scale, mFuseView.x+mFuseView.bitmap.getWidth()/2, mFuseView.y+mFuseView.bitmap.getHeight()/2);
canvas.drawBitmap(mFuseView.bitmap, mFuseView.x, mFuseView.y, mPaint);
canvas.restore();
dx += dip2px(4);
} else { // 直线上升
canvas.save();
canvas.scale(mFuseView.scale, mFuseView.scale, mFuseView.x+mFuseView.bitmap.getWidth()/2, mFuseView.y+mFuseView.bitmap.getHeight()/2);
canvas.drawBitmap(mFuseView.bitmap, mFuseView.x, mFuseView.y, mPaint);
canvas.restore();
mFuseView.y -= dip2px(5);
}
postDelayed(mDrawTask, 5);
} else {
if (!mIsDismiss) {
mIsDismiss = mBubbles.size() > 0 && mBubbles.get(0).top < mWidth*9/20;
}
for (int i=mBubbles.size()-1; i>=0; i--) { // 绘制气泡
drawBubble(canvas, mBubbles.get(i));
}
// 大爱心放大消失
mFuseView.x = mBombX - mFuseView.bitmap.getWidth()/2;
mFuseView.y = mBombY - mFuseView.bitmap.getHeight()/2;
mFuseView.scale += 0.2;
mFuseView.alpha = (int)((5-mFuseView.scale)*85);// ps:85 = 255/3 根据缩放倍数来计算透明度
if (mFuseView.alpha > 0) {
mPaint.setAlpha(mFuseView.alpha);
canvas.save();
canvas.scale(mFuseView.scale, mFuseView.scale, mBombX, mBombY);
canvas.drawBitmap(mFuseView.bitmap, mFuseView.x, mFuseView.y, mPaint);
canvas.restore();
}
if (mBubbles.size() > 0) {
postDelayed(mDrawTask, 10);
} else {
release();
}
}
}
}
private void drawBubble(Canvas canvas, Bubble bubble) {
if (bubble.top + bubble.bitmap.getHeight()/2 > mWidth/2) {
bubble.top = bubble.top - dip2px(2);
return;
}
if (mIsDismiss) {
bubble.alpha -= 12;
if (bubble.alpha < 0) {
mBubbles.remove(bubble);
return;
}
}
bubble.top -= dip2px(0.5f);
if (bubble.scale < 1.2f) {
bubble.scale += 0.015f;
}
mPaint.setAlpha(bubble.alpha);
canvas.save();
canvas.scale(bubble.scale, bubble.scale, bubble.left+bubble.bitmap.getWidth()/2, bubble.top+bubble.bitmap.getHeight()/2);
canvas.rotate(bubble.rotate, mBombX, mBombY);
canvas.drawBitmap(bubble.bitmap, bubble.left, bubble.top, mPaint);
canvas.restore();
}
/** * 开始绘制爆炸彩蛋,如果上一个效果还没结束,则不处理新的 */
public void startBomb() {
if (mFuseView != null) {
return;
}
initFuseView();
generateBubble(MAX_BUBBLE_COUNT);
dx = 0;
mIsDismiss = false;
post(mDrawTask);
}
/** * 初始化引导 */
private void initFuseView() {
mFuseView = new FuseView();
mFuseView.x = mWidth*7/8;
mFuseView.y = mHeight*17/20;
mFuseView.bitmap = BitmapUtil.readBitmap(getResources(), R.drawable.praise_five);
}
/** * 添加点赞,会对传入的个数进行处理 * @param count */
private void generateBubble(int count) {
for (int i = 0; i < count; i++) {
Bubble bubble = new Bubble();
bubble.bitmap = getRandBitmap();
bubble.alpha = 155 + mRandom.nextInt(100);
bubble.scale = 0.6f + mRandom.nextFloat()*0.4f;
// bubble.rotate = mRandom.nextInt(360);
bubble.rotate = 360*i/count; // 由于绘制时还需要缩放,不然可以使用增量旋转,即不用每次都restore
if (bubble.rotate > 180) { // 反向旋转
bubble.rotate = bubble.rotate - 360;
}
bubble.left = mWidth/2 - bubble.bitmap.getWidth()/2;
float offset = 0;
if (i % 2 == 0) {
offset = mWidth*0.1f + mWidth*0.15f * i/count;
}
if (i%3 == 0) {
offset = mWidth*0.25f + mWidth*0.15f * i/count;
}
if (i%5 == 0) {
offset = mWidth*0.4f + mWidth*0.12f * mRandom.nextFloat();
}
bubble.top = offset - bubble.bitmap.getHeight();
mBubbles.add(0, bubble);
}
mBubbles.get(0).top = mWidth*0.52f - dip2px(5); // 以第一个气泡的位置判断是否该dismiss
mBubbles.get(0).alpha = 1; // 防止正在消失时突然显现出来
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
this.mWidth = width;
this.mHeight = height;
mBombX = mWidth/2;
mBombY = mWidth/2;
}
class Bubble {
public Bitmap bitmap;
public float scale = 1.0f; // 缩放
public float top = 0f; // 偏移
public float left = 0f; // 偏移
public int rotate = 0; // 旋转
public int alpha = 255; // 透明度
public Bubble() {
this.bitmap = getRandBitmap();
}
}
class FuseView {
public Bitmap bitmap;
public float scale;
public int alpha;
public int x;
public int y;
}
// 抛物线
private float getY(float x) {
return mHeight*10/11 - 0.009f * x * x;
}
public void release() {
mFuseView = null; // 防止过度绘制
for (Bitmap bitmap : mDrawables) {
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
}
}
}
代码链接:https://github.com/brian512/AndroidDemo
我写的编程博客客户端介绍:http://blog.csdn.net/brian512/article/details/51558809