圣诞雪花纷飞自定义View
来源:互联网 发布:网络推广的方式有哪些 编辑:程序博客网 时间:2024/04/29 10:25
github地址:https://github.com/shuaijia/JSBaseDemo/blob/master/app/src/main/java/com/jia/demo/view/SnowView.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方法省略}
实现思路:
说起这种雪花纷飞的效果,大家都会立刻想到用属性动画,通过各种动画组合、插值器的使用(当然使用贝塞尔曲线会更炫),就可以很轻松的实现如上效果,但我们今天换种思路来实现:
因为所有的雪花需要实时在移动位置,所以想开启子线程去控制所以雪花位置,但因为在子线程中刷新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) { // Start editing the pixels in the surface. The returned Canvas can be used to draw into the surface's bitmap. 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了!
获取更多精彩内容,关注我的微信工作公众号——安卓干货营!
阅读全文