SurfaceView攻略~~~从零开始实现打雷下雨天气特效~①

来源:互联网 发布:360软件管家字体模糊 编辑:程序博客网 时间:2024/04/29 21:50
先说说写此文的背景:我以前初学android的时候,有琢磨这个SurfaceView,但是那时候真的是对android原理,多线程等等一点都不是很理解.照着网上的代码生搬硬套加上问师父,强行实现了几个功能,但是真真的理解很浅.这次公司需求写一个随着天气而改变背景的效果,但是又觉得放动画影响性能,公司的高级工程师大人要求我帮他写个界面.(艹公司说我是专职写界面的).好吧.那还能怎样,又不肯用动画,甚至差点连图片都不想用,啥都要我用onDraw画,只能掏出阔别已久的surfaceView了.先讲讲自定义SurfaceView和自定义View的区别吧.其实有写过自定义View的同学应该对于自定义View这一套也有点理解.简单来讲surfaceView就相当于View的进阶,View是不会主动变化的,是存在于UI线程了.而SurfaceView的自定义方式和View差不多,区别就在于SurfaceView它存在的目的就是显示那些可以动来动去的东西.那么这个东西动来动去的话,显然是不能让他存在于Ui线程的(否则就堵塞主线程啦).好的.那么我们开始吧.

注:(本来以为可以很快写完,结果不小心很晚了,还是分几篇写好了.第一篇主要是用最通俗的语言来论述surfaceView的基本写法和理解,如果只是来寻找打雷下雨的特效的话,敬请期待第二篇)
第一步.extends SurfaceView
继承surfaceView拿到它的构造函数.获得Context和与xml交互的AttributeSet.AttributeSet这里我就先省略了,咱们做的是功能,封装的事情暂且不提.

第二步. implements SurfaceHolder.Callback
这个回调是API现成的,它回调了SurfaceView的三大生命周期,onCreate,onChange,onDestroy.想来看字面意思各位就已经看得懂了.值得一提的是onDestroy.对于一些数据例如Bitmap等,都不要忘记在destroy的时候进行回收.

第三步.画画前的准备.
自定义控件比较有意思的地方就在于.它给你canvas(画布),又给了你paint(画笔),所以我特别喜欢说咱们来画画吧~特别符合常年被鄙视”你们就是画界面的”的android程序员.

是的.在画画前.先准备好你的画笔和画布.

画布是什么呢?
当然是我们美丽的画的容器啦.
画笔又是什么呢?
Paint greenPaint = new Paint();
我要画西瓜皮,我拿一个扁扁的,绿色的笔,满满的原谅色.
然后我画西瓜肉,我拿一个粗粗的,红色的笔,满满的垂涎欲滴.
画不同的画面,用不同的笔.

第四步.怎么开始画.
首先,咱们要架好咱们的画架.不抓稳了,不仅画不稳,说不定画板还会掉地上摔一身泥不是?
对于android而言.由于surfaceView是在子线程上进行操作.所以非常有必要注重多线程的安全性.所以在绘画前.要保障在之前通过callback获取到的回调正确地传达到咱们的画布上面.所以开始上代码:
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setFormat(PixelFormat.TRANSPARENT);
Canvas canvas = mHolder.lockCanvas()
这样,咱们就把画布在画板上固定好啦~!

那么,现在,我们掏出我们的原谅绿的画笔,来画一个圆.
重写onDraw()方法,我们开始了:
canvas.drawCircle(圆心X坐标,圆心Y坐标,半径,greenPaint);
这些个方法的实现方式也挺符合的逻辑的—-在画板上画(画在哪里,用什么笔画).
所有的绘画图形的参数基本大同小异,大伙看api提示或者百度一下(为了表示不low还要提一下谷歌)基本就可以完全搞懂.
同一支笔可以画不同的图案,也可以用不同的笔换不同的图案.但是你别把画布都换了就行.
另外郑重提示:作为一名合格的画家,开始画画就要心无旁骛做好准备,不要在画的时候找笔找纸找墨水(所有的实例化相关的生成相关的可服用的东西都不要在onDraw里面进行)

第五步.保存好咱们画好的画
画完了怎么办,拔腿就跑?不怕被偷走吗?就算你画的丑没人要,沾上灰总是不好的.
还记得我们的画布被固定在画板上嘛?
mHolder.unlockCanvasAndPost(canvas);
从画板上取下你的画布.
好的,恭喜你,到目前为止.第一帧画完了.

第六步:什么是第一帧?
废话,你要画的是会动的会变的东西.你画一张图有什么用?稍微了解过动画的大家估计都知道,所谓的动画,就是利用人的反应速度的延迟,以极快的速度切换画布产生位移效果让用户产生画面动起来了的效果.
纳尼?那岂不是要画无数张?
当然不.循环你总听说过吧?

第七步:循环绘制:
这时候有人要说了,无限循环啊!我会啊!while(true)!
好啊,那哥们,你停不下来啊!是的,这个时候就是要用上我们平时用来停止线程工作的标记了.我每次循环的时候都判断,你喊停我就停,对不对?
/**
* 绘制线程
*/
private class DrawThread extends Thread {

    // 用来停止线程的标记    private boolean isRunning = false;    void setRunning(boolean running) {        isRunning = running;    }    @Override    public void run() {        Canvas canvas;        // 无限循环绘制        while (isRunning) {            if (mType != null && mViewWidth != 0 && mViewHeight != 0) {                canvas = mHolder.lockCanvas();//首先锁定画布,然后执行绘制                if (canvas != null) {                    mType.onDraw(canvas);//绘制.在绘制的时候canvas必须处于锁定状态                    if (isRunning) {                        mHolder.unlockCanvasAndPost(canvas);//解锁canvas.                    } else {                        // 停止线程                        break;                    }                    // sleep                    SystemClock.sleep(16);//通过sleep的方式,刷新一帧surfaceView                }            }        }    }}

好的,这样的话,咱们就实现了每隔16毫秒(60ms)绘制一次图片.
这里只是利用咱们自己的循环来进行绘制,还可以利用各种android自带的传感器例如重力传感器等等等的回调来刷新界面,只需要设置这些传感器的回调的频率,即可实现手写循环一样的效果.

原创粉丝点击