github地址:github.com/shuaijia/JS…java
/**
* Description: 雪花效果實體類
* Created by jia on 2017/12/25.
* 人之因此能,是相信能
*/
public class Snow {
private float x;
private float y;
private int alfa;
private float size;
private float speed;
private int srcType;
public Snow(float x, float y, int alfa, float size, float speed, int srcType) {
this.x = x;
this.y = y;
this.alfa = alfa;
this.size = size;
this.speed = speed;
this.srcType = srcType;
}
...// get、set方法省略
}
複製代碼
實現思路:git
提及這種雪花紛飛的效果,你們都會馬上想到用屬性動畫,經過各類動畫組合、插值器的使用(固然使用貝塞爾曲線會更炫),就能夠很輕鬆的實現如上效果,但咱們今天換種思路來實現:github
由於全部的雪花須要實時在移動位置,因此想開啓子線程去控制因此雪花位置,但由於在子線程中刷新view,就採用SurfaceView來實現。canvas
SurfaceView繼承之View,但擁有獨立的繪製表面,即它不與其宿主窗口共享同一個繪圖表面,能夠單獨在一個線程進行繪製,並不會佔用主線程的資源。bash
public SnowView(Context context, AttributeSet attrs) {
super(context, attrs);
surfaceHolder = this.getHolder();
surfaceHolder.addCallback(this);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.snow_bg);
init();
}
private void init() {
DisplayMetrics dm = getResources().getDisplayMetrics();
snows = new ArrayList<>();
float x, y, size, speed;
int alfa, srcType;
for (int i = 0; i < maxCount; i++) {
x = (float) Math.floor(Math.random() * dm.widthPixels);//初始X座標
y = (float) Math.floor(Math.random() * dm.heightPixels);//初始Y座標
size = (float) ((Math.random() * 15f) + 20f);//初始半徑
speed = (float) ((Math.random() * 6) + 5);
alfa = (int) Math.floor(100 * Math.random() + 155);
srcType = (int) (Math.random() + 0.5);
snows.add(new Snow(x, y, alfa, size, speed, srcType));
}
}
複製代碼
初始化SnowView,咱們定義一屏雪花數(如100),循環100次,使用隨機數設置雪花位置、大小、透明度等屬性,並放入集合中。微信
/**
* 繪製進程
*/
class DrawThread extends Thread {
public boolean isRunning = false;
private Canvas canvas;
public DrawThread() {
isRunning = true;
}
@Override
public void run() {
super.run();
while (isRunning) {
synchronized (surfaceHolder) {
canvas = surfaceHolder.lockCanvas();
drawSprite(canvas);
for (int i = 0; i < maxCount; i++) {
curSnow = snows.get(i);
float size = curSnow.getSize();
float speed = curSnow.getSpeed();
int alfa = curSnow.getAlfa();
float x = curSnow.getX();
float y = curSnow.getY() + speed;
int type = curSnow.getSrcType();
if (y >= canvas.getHeight() || x >= canvas.getWidth()) {
y = 0;
x = (float) Math.floor(Math.random() * canvas.getWidth());//初始X座標
}
mPaint.setAlpha(alfa);
Bitmap snowBitmap;
if (type == 1) {
snowBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.snow1);
} else {
snowBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.snow2);
}
RectF rect = new RectF(x, y, x + size, y + size);
canvas.drawBitmap(snowBitmap, null, rect, mPaint);
snows.set(i, new Snow(x, y, alfa, size, speed, type));
}
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
public void stopThread() {
isRunning = false;
boolean workIsNotFinish = true;
while (workIsNotFinish) {
try {
this.join();// 保證run方法執行完畢
} catch (InterruptedException e) {
e.printStackTrace();
}
workIsNotFinish = false;
}
}
}
private void drawSprite(Canvas canvas) {
//清屏操做
canvas.drawBitmap(bgBitmap, null, new Rect(0, 0, canvas.getWidth(), canvas.getHeight()), null);
}
複製代碼
在run方法中獲取到當前繪製的canvas,而後循環進行繪製,繪製完成後surfaceHolder.unlockCanvasAndPost(canvas),將畫布顯示在屏幕上。app
注意整個循環執行次數多,但咱們必須保證所有繪製完再切換線程,因此咱們使用synchronized關鍵字。dom
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (null == mDrawThread) {
mDrawThread = new DrawThread();
mDrawThread.start();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (null != mDrawThread) {
mDrawThread.stopThread();
}
}
複製代碼
在surface建立的回調中開啓線程,在destroy方法中關閉線程,就ok了!ide
獲取更多精彩內容,關注個人微信工做公衆號——Android機動車!動畫