效果图
、
二、代码实现
public class FireworksView extends View implements Runnable { private static final long V_SYNC_TIME = 50; private final DisplayMetrics mDM; private TextPaint mArcPaint; private List<FireDot> fireDots = new ArrayList<>(); private List<Class<? extends Drawer>> registry = new ArrayList<>(); private int maxDashSize = 200; private long displayTime = 1_000L; private long currentDisplayTime = -1; private boolean isNextDrawingTimeScheduled = false; private boolean isStartUpdate = false; private Random radiusRnd = null; private TextPaint mDrawerPaint = null; private static final int TYPE_EXPLORE = 0; private static final int TYPE_EXPAND = 1; private static final int TYPE_EXPAND_OUTLINE = 2; private int type = TYPE_EXPAND_OUTLINE; private int maxRadius; private int inlineRadius; public FireworksView(Context context) { this(context, null); } public FireworksView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FireworksView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mDM = getResources().getDisplayMetrics(); initPaint(); setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { addRegistry(CircleDrawer.class); startPlay(); } }); } public static class CircleDrawer implements Drawer{ int color = Color.BLACK; public CircleDrawer(){ color = argb((float) Math.random(),(float)Math.random(),(float)Math.random()); } @Override public void draw(Canvas canvas, Paint paint, FireDot dot) { int origin = paint.getColor(); paint.setColor(color); canvas.drawCircle(dot.x,dot.y,3,paint); paint.setColor(origin); } public int argb( float red, float green, float blue) { return ((int) (1 * 255.0f + 0.5f) << 24) | ((int) (red * 255.0f + 0.5f) << 16) | ((int) (green * 255.0f + 0.5f) << 8) | (int) (blue * 255.0f + 0.5f); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); if (widthMode != MeasureSpec.EXACTLY) { widthSize = mDM.widthPixels / 2; } int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (heightMode != MeasureSpec.EXACTLY) { heightSize = widthSize / 2; } radiusRnd = new Random(SystemClock.currentThreadTimeMillis()); setMeasuredDimension(widthSize, heightSize); } private boolean isDebug() { return true; } public float dp2px(float dp) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDM); } public float sp2px(float dp) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, dp, mDM); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = getWidth(); int height = getHeight(); if (width == 0 || height == 0) { return; } maxRadius = Math.min(width / 2, height / 2); inlineRadius = Math.min(width / 2, height / 2) / 5; int saveCount = canvas.save(); canvas.translate(width / 2, height / 2); //canvas.drawCircle(0, 0, inlineRadius, mArcPaint); long t = getNextDrawingTime(); if (t != -1) { for (FireDot dot : fireDots) { if(isStartUpdate){ double degree = Math.random() * 360; if( degree> 360 ){ degree = 360; } double radians = Math.toRadians(degree); int radius = radiusRnd.nextInt(inlineRadius/2) + 1; float x = (float) (Math.cos(radians) * radius); float y = (float) (Math.sin(radians) * radius); dot.init(x,y,displayTime,radius,radians); } dot.t = t; onDrawEffect(dot,canvas,mDrawerPaint); } isStartUpdate = false; if(!isNextDrawingTimeScheduled) { isNextDrawingTimeScheduled = true; postDelayed(this, V_SYNC_TIME); } } canvas.restoreToCount(saveCount); } protected void onDrawEffect(FireDot dot,Canvas canvas,Paint paint){ if(type==TYPE_EXPLORE){ exploreDraw(dot,canvas,paint); }else if(type==TYPE_EXPAND){ expandDraw(dot,canvas,paint); }else if(type==TYPE_EXPAND_OUTLINE){ expandToOutline(dot,canvas,paint); } } @Override public void run() { isNextDrawingTimeScheduled = false; if (currentDisplayTime < displayTime) { currentDisplayTime = Math.min(displayTime,currentDisplayTime+V_SYNC_TIME); } else { currentDisplayTime = -1; } postInvalidate(); } private long getNextDrawingTime() { return currentDisplayTime; } public <T extends Drawer> void addRegistry( Class<T> drawer) { if (!this.registry.contains(drawer)) { this.registry.add(drawer); } } public void startPlay() { fillFireDashes(); isStartUpdate = true; currentDisplayTime = -1; removeCallbacks(this); run(); } private void fillFireDashes() { for (Class< ? extends Drawer> drawable : this.registry) { try { FireDot dash = new FireDot(drawable.newInstance()); if (fireDots.size() >= maxDashSize) { break; } fireDots.add(dash); } catch (Exception e) { e.printStackTrace(); } } Random random = new Random(SystemClock.currentThreadTimeMillis()); while (fireDots.size() < maxDashSize) { int nextInt = random.nextInt(this.registry.size()); try { Class<? extends Drawer> drawable = registry.get(nextInt); FireDot dash = new FireDot(drawable.newInstance()); fireDots.add(dash); } catch (Exception e) { e.printStackTrace(); } } } public void setMaxDashSize(int maxDashSize) { this.maxDashSize = maxDashSize; } private void initPaint() { // 实例化画笔并打开抗锯齿 mArcPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mArcPaint.setAntiAlias(true); mArcPaint.setStyle(Paint.Style.STROKE); mArcPaint.setStrokeCap(Paint.Cap.ROUND); mDrawerPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mDrawerPaint.setAntiAlias(true); mDrawerPaint.setStyle(Paint.Style.FILL); mDrawerPaint.setStrokeCap(Paint.Cap.ROUND); } public void setDisplayTime(long displayTime) { this.displayTime = displayTime; } private void exploreDraw(FireDot dot,Canvas canvas, Paint paint) { double speed = dot.radius*10F / displayTime; dot.x = (float) ((dot.radius + (speed * dot.t)) * Math.cos(dot.radians)); dot.y = (float) ((dot.radius + (speed * dot.t)) * Math.sin(dot.radians)); dot.drawer.draw(canvas,paint,dot); } private void expandToOutline(FireDot dot,Canvas canvas, Paint paint) { float speed = maxRadius * 1F / displayTime; double R = dot.radius + (speed * dot.t); if(R <= 2*inlineRadius) { dot.x = (float) ((dot.radius + (speed * dot.t)) * Math.cos(dot.radians)); dot.y = (float) ((dot.radius + (speed * dot.t)) * Math.sin(dot.radians)); } dot.drawer.draw(canvas,paint,dot); } private void expandDraw(FireDot dot,Canvas canvas, Paint paint) { float C = (float) (inlineRadius/(inlineRadius - dot.radius)); if(C>4){ C = 4; } float speed = (float) ((maxRadius * 1F / dot.duration) +(C* (inlineRadius /dot.duration))); dot.x = (float) ((dot.radius + (speed * dot.t))*Math.cos(dot.radians)); dot.y = (float) ((dot.radius + (speed * dot.t))*Math.sin(dot.radians)); dot.drawer.draw(canvas,paint,dot); } private static class FireDot { private final Drawer drawer; private float x; private float y; private long duration; private float startX; private float startY; private double radius; private double radians; private long t; public FireDot(Drawer drawer) { this.drawer = drawer; } public void init(float x, float y, long duration,float radius, double radians) { this.x = x; this.y = y; this.startX = x; this.startY = y; this.duration = duration; this.radius = radius; this.radians = radians; } } public interface Drawer { void draw(Canvas canvas,Paint paint,FireDot dot); } }