diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7f1bf94..00a0fee 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -39,6 +39,7 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/com/demo/widget/meis/MainActivity.java b/app/src/main/java/com/demo/widget/meis/MainActivity.java
index 352aa1d..abce5c0 100644
--- a/app/src/main/java/com/demo/widget/meis/MainActivity.java
+++ b/app/src/main/java/com/demo/widget/meis/MainActivity.java
@@ -58,4 +58,8 @@ public void onMokibe(View view) {
public void onRose(View view) {
startActivity(new Intent(this, MeiRoseActivity.class));
}
+
+ public void onFirefly(View view) {
+ startActivity(new Intent(this, MeiFireflyActivity.class));
+ }
}
diff --git a/app/src/main/java/com/demo/widget/meis/MeiFireflyActivity.java b/app/src/main/java/com/demo/widget/meis/MeiFireflyActivity.java
new file mode 100644
index 0000000..035a61a
--- /dev/null
+++ b/app/src/main/java/com/demo/widget/meis/MeiFireflyActivity.java
@@ -0,0 +1,25 @@
+package com.demo.widget.meis;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+
+import com.demo.widget.R;
+import com.demo.widget.utils.Eyes;
+import com.meis.widget.particle.FireflyView;
+
+/**
+ * Created by wenshi on 2018/7/5.
+ * Description 浮动粒子界面
+ */
+public class MeiFireflyActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.mei_fire_fly_activity);
+ Eyes.translucentStatusBar(this, true);
+ }
+}
diff --git a/app/src/main/res/layout/mei_fire_fly_activity.xml b/app/src/main/res/layout/mei_fire_fly_activity.xml
new file mode 100644
index 0000000..b98e2d5
--- /dev/null
+++ b/app/src/main/res/layout/mei_fire_fly_activity.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/meis_activity.xml b/app/src/main/res/layout/meis_activity.xml
index 91d4447..a5e33bf 100644
--- a/app/src/main/res/layout/meis_activity.xml
+++ b/app/src/main/res/layout/meis_activity.xml
@@ -192,5 +192,30 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_firefly_bg.png b/app/src/main/res/mipmap-xxxhdpi/ic_firefly_bg.png
new file mode 100644
index 0000000..bfdd992
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_firefly_bg.png differ
diff --git a/widget/src/main/java/com/meis/widget/particle/FireflyView.java b/widget/src/main/java/com/meis/widget/particle/FireflyView.java
new file mode 100644
index 0000000..65f5088
--- /dev/null
+++ b/widget/src/main/java/com/meis/widget/particle/FireflyView.java
@@ -0,0 +1,164 @@
+package com.meis.widget.particle;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import com.meis.widget.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Created by wenshi on 2018/7/5.
+ * Description 浮点粒子控件
+ */
+public class FireflyView extends SurfaceView implements SurfaceHolder.Callback {
+
+ // 粒子的最大数量
+ private static final int MAX_NUM = 400;
+ // 粒子集合
+ private List mListParticles;
+ // 随机数
+ private Random mRandom;
+
+ private SurfaceHolder mHolder;
+
+ // 动画线程
+ private Handler mHandler;
+
+ // 粒子半径
+ private int mParticleMaxRadius;
+
+ // 粒子数量
+ private int mParticleNum;
+
+ // 粒子移动速率
+ private int mParticleMoveRate;
+
+ private static final int EMPTY_FLAG = 1;
+
+ public FireflyView(Context context) {
+ this(context, null);
+ }
+
+ public FireflyView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FireflyView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ // 关闭硬件加速
+ setLayerType(LAYER_TYPE_SOFTWARE, null);
+ init();
+
+ TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FireflyView);
+ mParticleMaxRadius = ta.getInt(R.styleable.FireflyView_firefly_max_radius, 5);
+ mParticleNum = ta.getInt(R.styleable.FireflyView_firefly_num, MAX_NUM);
+ mParticleMoveRate = ta.getInt(R.styleable.FireflyView_firefly_move_rate, 5);
+ ta.recycle();
+ }
+
+ private void init() {
+ // 设置透明
+ setZOrderOnTop(true);
+ // 配合清屏 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+ mHolder = getHolder();
+ mHolder.setFormat(PixelFormat.TRANSLUCENT);
+ mHolder.addCallback(this);
+ // 初始化随机数
+ mRandom = new Random();
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ int measuredWidth = getMeasuredWidth();
+ int measureHeight = getMeasuredHeight();
+
+ initParticlesData(measuredWidth, measureHeight);
+ startAnimation();
+ }
+
+ // 初始化浮点粒子数据
+ private void initParticlesData(int width, int height) {
+ mListParticles = new ArrayList<>();
+ for (int i = 0; i < mParticleNum; i++) {
+ FloatParticle fp = new FloatParticle(width, height);
+ mParticleMaxRadius = mParticleMaxRadius < 2 ? 2 : mParticleMaxRadius;
+ fp.setRadius(mRandom.nextInt(mParticleMaxRadius - 1) + 1);
+ mListParticles.add(fp);
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ stopAnimation();
+ }
+
+ public void stopAnimation() {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ public void startAnimation() {
+ if (mHandler != null) return;
+ HandlerThread fireThread = new HandlerThread(this.getClass().getName());
+ fireThread.start();
+ mHandler = new Handler(fireThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ Canvas mCanvas = mHolder.lockCanvas(null);
+ if (mCanvas != null) {
+ synchronized (mHolder) {
+ // 清屏
+ mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+ for (FloatParticle fp : mListParticles) {
+ fp.drawParticle(mCanvas);
+ }
+ }
+ }
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (mCanvas != null) {
+ mHolder.unlockCanvasAndPost(mCanvas);
+ }
+ mHandler.sendEmptyMessageDelayed(EMPTY_FLAG, mParticleMoveRate);
+ }
+ };
+ mHandler.sendEmptyMessage(EMPTY_FLAG);
+ }
+
+ public int getParticleMoveRate() {
+ return mParticleMoveRate;
+ }
+
+ public void setParticleMoveRate(int particleMoveRate) {
+ mParticleMoveRate = particleMoveRate;
+ }
+
+ public int getParticleMaxRadius() {
+ return mParticleMaxRadius;
+ }
+
+ public int getParticleNum() {
+ return mParticleNum;
+ }
+}
diff --git a/widget/src/main/java/com/meis/widget/particle/FloatParticle.java b/widget/src/main/java/com/meis/widget/particle/FloatParticle.java
new file mode 100644
index 0000000..7b65bdb
--- /dev/null
+++ b/widget/src/main/java/com/meis/widget/particle/FloatParticle.java
@@ -0,0 +1,159 @@
+package com.meis.widget.particle;
+
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.Point;
+
+import java.util.Random;
+
+/**
+ * Created by wenshi on 2018/7/4.
+ * Description 浮点粒子
+ */
+public class FloatParticle {
+
+ // 三阶贝塞尔曲线
+ private Point startPoint;
+ private Point endPoint;
+ private Point controlPoint1;
+ private Point controlPoint2;
+
+ private Paint mPaint;
+ private Path mPath;
+ private Random mRandom;
+
+ // 圆半径
+ private float mRadius = 5;
+
+ // 控件宽度
+ private int mWidth;
+ // 控件高度
+ private int mHeight;
+
+ private float mCurDistance = 0;
+
+ private static final int DISTANCE = 255;
+
+ private static final float MOVE_PER_FRAME = 1f;
+
+ // 火花外侧阴影大小
+ private static final float BLUR_SIZE = 5.0F;
+
+ // 路径测量
+ private PathMeasure mPathMeasure;
+
+ private float mMeasureLength;
+
+ public FloatParticle(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ mRandom = new Random();
+
+ startPoint = new Point((int) (mRandom.nextFloat() * mWidth), (int) (mRandom.nextFloat() * mHeight));
+
+ // 抗锯齿
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setColor(Color.WHITE);
+ // 防抖动
+ mPaint.setDither(true);
+ mPaint.setStyle(Paint.Style.FILL);
+ // 设置模糊效果 边缘模糊
+ mPaint.setMaskFilter(new BlurMaskFilter(BLUR_SIZE, BlurMaskFilter.Blur.SOLID));
+
+ mPath = new Path();
+ mPathMeasure = new PathMeasure();
+
+ startPoint.x = (int) (mRandom.nextFloat() * mWidth);
+ startPoint.y = (int) (mRandom.nextFloat() * mHeight);
+ }
+
+ public void drawParticle(Canvas canvas) {
+
+ // 初始化三阶贝塞尔曲线数据
+ if (mCurDistance == 0) {
+ endPoint = getRandomPointRange(startPoint.x, startPoint.y, DISTANCE);
+ controlPoint1 = getRandomPointRange(startPoint.x, startPoint.y, mRandom.nextInt(Math.min(mWidth, mHeight) / 2));
+ controlPoint2 = getRandomPointRange(endPoint.x, endPoint.y, mRandom.nextInt(Math.min(mWidth, mHeight) / 2));
+ // 添加贝塞尔曲线路径
+ mPath.reset();
+ mPath.moveTo(startPoint.x, startPoint.y);
+ mPath.cubicTo(controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);
+ mPathMeasure.setPath(mPath, false);
+ mMeasureLength = mPathMeasure.getLength();
+ }
+ //计算当前坐标点
+ float[] loc = new float[2];
+ mPathMeasure.getPosTan(mCurDistance / DISTANCE * mMeasureLength, loc, null);
+ startPoint.x = (int) loc[0];
+ startPoint.y = (int) loc[1];
+
+ // 递增1
+ mCurDistance += MOVE_PER_FRAME;
+
+ if (mCurDistance >= DISTANCE) {
+ mCurDistance = 0;
+ }
+
+ canvas.drawCircle(startPoint.x, startPoint.y, mRadius, mPaint);
+ }
+
+ /**
+ * @param baseX 基准坐标x
+ * @param baseY 基准坐标y
+ * @param range 指定范围长度
+ * @return 根据基准点获取指定范围的随机点
+ */
+ private Point getRandomPointRange(int baseX, int baseY, int range) {
+ int randomX = 0;
+ int randomY = 0;
+ //range指定长度为255,可以根据实际效果调整
+ if (range <= 0) {
+ range = 1;
+ }
+ //我们知道一点(baseX,baseY)求与它距离长度为range的另一点
+
+ //两点x方向的距离(随机产生)
+ int distanceX = mRandom.nextInt(range);
+
+ //知道x方向的距离与斜边的距离求y方向的距离
+ int distanceY = (int) Math.sqrt(range * range - distanceX * distanceX);
+
+ randomX = baseX + getRandomPNValue(distanceX);
+ randomY = baseY + getRandomPNValue(distanceY);
+
+ if (randomX > mWidth) {
+ randomX = mWidth - range;
+ } else if (randomX < 0) {
+ randomX = range;
+ } else if (randomY > mHeight) {
+ randomY = mHeight - range;
+ } else if (randomY < 0) {
+ randomY = range;
+ }
+
+ return new Point(randomX, randomY);
+ }
+
+ /**
+ * 获取随机的正负值
+ *
+ * @return
+ */
+ private int getRandomPNValue(int value) {
+ return mRandom.nextBoolean() ? value : 0 - value;
+ }
+
+ /**
+ * 设置圆半径
+ *
+ * @param radius
+ */
+ public void setRadius(float radius) {
+ mRadius = radius;
+ }
+}
diff --git a/widget/src/main/res/values/attrs.xml b/widget/src/main/res/values/attrs.xml
index 61f435f..8ea2a5b 100644
--- a/widget/src/main/res/values/attrs.xml
+++ b/widget/src/main/res/values/attrs.xml
@@ -74,4 +74,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file