Github address: github.com/shuaijia/JS…

Let’s define the entity class:

/** * Created by jia on 2017/12/25. */ public class Snow {privatefloat 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, floatspeed, int srcType) { this.x = x; this.y = y; this.alfa = alfa; this.size = size; this.speed = speed; this.srcType = srcType; }... / / get,setMethod omitted}Copy the code

Implementation idea:

When it comes to this kind of effect, everyone immediately thinks of using attribute animation, which can be easily achieved by using various animation combinations and interpolators (of course, using Bezier curves is even more cool), but today we will use a different way to achieve this effect:

Since all snowflakes need to move in real time, we want to open the child thread to control the position of all snowflakes, but because the view is refreshed in the child thread, we use the SurfaceView to achieve.

SurfaceView inherits the View, but has a separate drawing surface, that is, it does not share the same drawing surface with its host window, and can be drawn in a separate thread without occupying the main thread resources.

The custom 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); // the initial x-coordinate y = (float) Math.floor(Math.random() * dm.heightPixels); // initial Y coordinate size = (float) ((Math.random() * 15f) + 20f); // Initial radius 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));
        }
    }
Copy the code

To initialize SnowView, we define the number of snowflakes on a screen (such as 100), loop 100 times, use random number to set the position, size, transparency and other properties of snowflakes, and put them into the collection.

/** * extends Thread {public Boolean isRunning = string extends Threadfalse;
        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()); } 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(); } catch (InterruptedException e) {e.printStackTrace(); } workIsNotFinish =false; }} private void drawBitmap(Canvas Canvas) {Canvas. DrawBitmap (Canvas Canvas) {bgBitmap, null, new Rect(0, 0, canvas.getWidth(), canvas.getHeight()), null);

    }
Copy the code

In the run method to get the current drawing canvas, then cycle are drawn, the surfaceHolder. After unlockCanvasAndPost (canvas), will the canvas displayed on the screen.

Note that the entire loop is executed many times, but we must ensure that all draws are completed before switching threads, so we use the synchronized keyword.

    @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();
        }
    }
Copy the code

Start the thread in the callback created by the surface and close it in the destroy method, and you’re done!

Get more exciting content, pay attention to my wechat work public number – Android motor vehicle!