聖誕雪花紛飛自定義View

Android機動車發表於2019-03-02

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機動車

聖誕雪花紛飛自定義View

相關文章