github地址:github.com/shuaijia/JS…
先定義下實體類吧:
/**
* 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方法省略
}
複製程式碼
實現思路:
說起這種雪花紛飛的效果,大家都會立刻想到用屬性動畫,通過各種動畫組合、插值器的使用(當然使用貝塞爾曲線會更炫),就可以很輕鬆的實現如上效果,但我們今天換種思路來實現:
因為所有的雪花需要實時在移動位置,所以想開啟子執行緒去控制所以雪花位置,但因為在子執行緒中重新整理view,就採用SurfaceView來實現。
SurfaceView繼承之View,但擁有獨立的繪製表面,即它不與其宿主視窗共享同一個繪圖表面,可以單獨在一個執行緒進行繪製,並不會佔用主執行緒的資源。
自定義view
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),將畫布顯示在螢幕上。
注意整個迴圈執行次數多,但我們必須保證全部繪製完再切換執行緒,所以我們使用synchronized關鍵字。
@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了!
獲取更多精彩內容,關注我的微信工作公眾號——Android機動車!