ANDROID模拟火花粒子的滑动喷射效果
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持!
开篇废话:
年前换了一个手机,SONY的Z3C。这个手机在解锁屏幕时有一个滑动动画,类似火花的粒子喷射,效果很炫。。。
于是尝试着模拟了一下,完成后效果如下图(还有很多细节没有实现):

SurfaceView:
因为surfaceview是使用的双缓冲机制,所以很适合绘制这种需要不停变换的画面。
下面我从网上copy了几条关于SurfaceView的一些特性(已经表明了出处),因为写这个Demo的一个主要目的就是熟悉了解Android的绘图机制。
SurfaceView和View最本质的区别:
摘录自http://www.cnblogs.com/lipeil/archive/2012/08/31/2666187.html
surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。
那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中 thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。
所以基于以上,根据游戏特点,一般分成两类:
1 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
2 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。
双缓冲:
摘录自http://blog.csdn.net/tobacco5648/article/details/8261749
Android中的SurfaceView在更新视图时,为了提高更新效率,加强用户体验,采用了双缓存机制。
Android的官方说明:
Note: On each pass you retrieve the Canvas from the SurfaceHolder, the previous state of the Canvas will be retained. In order to properly animate your graphics, you must re-paint the entire surface. For example, you can clear the previous state of the Canvas
by filling in a color with drawColor() or setting a background image with drawBitmap(). Otherwise, you will see traces of the drawings you previously performed.
简单理解:
在运用时可以理解为:SurfaceView在更新视图时用到了两张Canvas,一张frontCanvas和一张backCanvas,每次实际显示的是frontCanvas,backCanvas存储的是上一次更改前的视图,当使用lockCanvas()获取画布时,得到的实际上是backCanvas而不是正在显示的frontCanvas,之后你在获取到的backCanvas上绘制新视图,再unlockCanvasAndPost(canvas)此视图,那么上传的这张canvas将替换原来的frontCanvas作为新的frontCanvas,原来的frontCanvas将切换到后台作为backCanvas。例如,如果你已经先后两次绘制了视图A和B,那么你再调用lockCanvas()获取视图,获得的将是A而不是正在显示的B,之后你讲重绘的C视图上传,那么C将取代B作为新的frontCanvas显示在SurfaceView上,原来的B则转换为backCanvas。
Surface使用方法:
(摘录自http://www.cnblogs.com/devinzhang/archive/2012/02/03/2337559.html)
1)实现步骤
a.继承SurfaceView
b.实现SurfaceHolder.Callback接口
2)需要重写的方法
(1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){} //在surface的大小发生改变时激发
(2)public void surfaceCreated(SurfaceHolder holder){} //在创建时激发,一般在这里调用画图的线程。
(3)public void surfaceDestroyed(SurfaceHolder holder) {} //销毁时激发,一般在这里将画图的线程停止、释放。</span>
3)SurfaceHolder
SurfaceHolder,surface的控制器,用来操纵surface。处理它的Canvas上画的效果和动画,控制表面,大小,像素等。几个需要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 给SurfaceView当前的持有者一个回调对象。
(2)、abstract Canvas lockCanvas();
// 锁定画布,一般在锁定后就可以通过其返回的画布对象Canvas,在其上面画图等操作了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 锁定画布的某个区域进行画图等..因为画完图后,会调用下面的unlockCanvasAndPost来改变显示内容。
// 相对部分内存要求比较高的游戏来说,可以不用重画dirty外的其它区域的像素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 结束锁定画图,并提交改变。</span>
4)总结整个过程
继承SurfaceView并实现SurfaceHolder.Callback接口 ---->
SurfaceView.getHolder()获得SurfaceHolder对象 ---->
SurfaceHolder.addCallback(callback)添加回调函数---->
SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布---->
Canvas绘画 ---->
SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。
代码实现:
介绍代码前先简单整理下思路:
1.在手指点击处会不停的喷射火花粒子
3.粒子喷射长度和密度不同,离触摸点越近越多,越远越少
3.粒子会从小变大,再从大到消失
4.粒子产生后会沿着某个方向随机运动
5.粒子会有淡淡的发光效果并且会变换颜色
下面来逐一进行讲解:
1.在手指点击处会不停的喷射火花粒子:
要处理手指按下首先需要设置触摸点击监听,解决方法就是在自定义的surfaceview中重写onTouchEvent方法,从而获取到手指的点击位置。
知道了触摸点后就需要生成火花粒子,这时候就需要使用画笔(Paint)和画板(Canvas)来画出他们。
设置画笔方法:
private void setSparkPaint()
{
this.mSparkPaint = new Paint();
// 打开抗锯齿
this.mSparkPaint.setAntiAlias(true);
/*
* 设置画笔样式为填充 Paint.Style.STROKE:描边 Paint.Style.FILL_AND_STROKE:描边并填充
* Paint.Style.FILL:填充
*/
this.mSparkPaint.setDither(true);
this.mSparkPaint.setStyle(Paint.Style.FILL);
// 设置外围模糊效果
this.mSparkPaint.setMaskFilter(new BlurMaskFilter(BLUR_SIZE, BlurMaskFilter.Blur.SOLID));
}
画笔设置好以后以后就用这个画笔在画布上画出这些粒子,这里为了简单都将粒子看作一个个小圆点。如下方法的作用就是在触摸点循环画出这些小圆
// 循环绘制所有火花
for (int[] n : sparks)
{
n = sparkManager.drawSpark(mCanvas, (int) X, (int) Y, n);
}
经过一些列计算后调用canvas画圆的方法来画出粒子:
// 画花火
canvas.drawCircle(bezierPoint.x, bezierPoint.y, radius, mSparkPaint);
2.粒子喷射长度和密度不同,离触摸点越近越多,越远越少
长度,和密度这两个可以通过随即函数来解决。
mDistance = getRandom(SparkView.WIDTH / 4, mRandom.nextInt(15)) + 1;
3.粒子会从小变大,再从大到消失:
之前尝试过根据粒子存在时间改变透明度的方法,但效果不好。
所以直接调整粒子的大小来更好。但需要两个阶段:
第一阶段,粒子小圆的半径从0到最大。
第二阶段,粒子小圆的半径从最大到0。
只需要动态的计算出半径,最后将半径传递到canvas.drawCircle中就可以完成这个效果。
因为半径的值需要均匀的增加,我的思路是通过路径长度和当前走过长度的比值再乘以一个速率得出:
/**
* 更新火花路径
*/
private void updateSparkPath()
{
mCurDistance += PER_SPEED_SEC;
// 前半段
if (mCurDistance < (mDistance / 2) && (mCurDistance != 0))
{
radius = SPARK_RADIUS * (mCurDistance / (mDistance / 2));
}
// 后半段
else if (mCurDistance > (mDistance / 2) && (mCurDistance < mDistance))
{
radius = SPARK_RADIUS - SPARK_RADIUS * ((mCurDistance / (mDistance / 2)) - 1);
}
// 完成
else if (mCurDistance >= mDistance)
{
mCurDistance = 0;
mDistance = 0;
radius = 0;
}
}
4.粒子产生后会沿着某个方向随机运动
这个就简单了,相信大家肯定能想到就是——赛贝尔曲线(关于赛贝尔曲线推荐参考维基百科)。
粒子路径可以通过一条4点的赛贝尔曲线模拟,如下图:
![]()
我在上网找了一个函数可以求出赛贝尔曲线在某时间比下的点:
/**
* 计算塞贝儿曲线
*
* @param t 时间,范围0-1
* @param s 起始点
* @param c1 拐点1
* @param c2 拐点2
* @param e 终点
* @return 塞贝儿曲线在当前时间下的点
*/
private Point CalculateBezierPoint( float t, Point s, Point c1, Point c2, Point e )
{
float u = 1 - t;
float tt = t * t;
float uu = u * u;
float uuu = uu * u;
float ttt = tt * t; Point p = new Point((int) (s.x * uuu), (int) (s.y * uuu));
p.x += 3 * uu * t * c1.x;
p.y += 3 * uu * t * c1.y;
p.x += 3 * u * tt * c2.x;
p.y += 3 * u * tt * c2.y;
p.x += ttt * e.x;
p.y += ttt * e.y; return p;
}
将计算后的点传入画圆函数中:
// 计算塞贝儿曲线的当前点
Point bezierPoint = CalculateBezierPoint(mCurDistance / mDistance, start, c1, c2, end);
// 画花火
canvas.drawCircle(bezierPoint.x, bezierPoint.y, radius, mSparkPaint);
5.粒子会有淡淡的发光效果并且会变换颜色
淡淡的发光和变换颜色都是通过画笔设置的:
淡淡发光:
this.mSparkPaint.setMaskFilter(new BlurMaskFilter(BLUR_SIZE, BlurMaskFilter.Blur.SOLID));
但需要关闭硬件加速:
// 关闭硬件加速
setLayerType(LAYER_TYPE_SOFTWARE, null);
设置随机变换颜色:
// 设置随机颜色
mSparkPaint.setColor(Color.argb(255, mRandom.nextInt(128) + 128, mRandom.nextInt(128) + 128, mRandom.nextInt(128) + 128));
总结:
大致逻辑已经介绍完毕,还有很多细节没有实现,并且发现有轻微的闪烁现象,但没有找到很好的解决办法(求大神指点)。。。
Github下载连接:
https://github.com/a396901990/SparkScreen/tree/master
最后推荐两篇关于SurfaceView和画图的文章:
如果通过SurfaceVIew做游戏Create
a SurfaceView Game step-by-step
爱哥的Android自定义控件其实很简单系列
ANDROID模拟火花粒子的滑动喷射效果的更多相关文章
- android中listview的item滑动删除效果(已解决listview点击问题)
领导看到iphone上tableview有个滑动删除的效果,要求在android上也实现,搜了下资料,实现起来比较简单,可弄到后面,居然不能点击了,把一篇文章中的代码修改了一下,捣鼓了一番,搞定,下面 ...
- 【Android UI】案例03滑动切换效果的实现(ViewPager)
本例使用ViewPager实现滑动切换的效果.本例涉及的ViewPager.为android.support.v4.view.ViewPager.所以须要在android项目中导入android-su ...
- 移动WEB模拟原声APP滑动删除
移动WEB模拟原声APP滑动删除 效果 代码 <!DOCTYPE html> <html lang="en"> <head> <meta ...
- Android实现滑动刻度尺效果,选择身高体重和生日
刻度尺效果虽然看起来很美,我个人认为很不实用,即使再不实用,也有用的,鉴于群里成员对我的苦苦哀求,我就分享一个他用不到的,横屏滑动刻度尺,因为他需要竖屏的,哈哈…… 最近群里的开发人员咨询怎样实现刻度 ...
- Android UI效果实现——Activity滑动退出效果
更新说明: 1.在QQ网友北京-旭的提醒下,在SlideFrame的initilize方法中添加了focusable.focusableInTouch.clickable的状态设置,否则会导致部分情况 ...
- 【转】Android 实现ListView的滑动删除效果
http://www.cnblogs.com/weixiao870428/p/3524055.html http://download.csdn.net/download/love_javc_you/ ...
- Android实现左右滑动指引效果
本文介绍Android中实现左右滑动的指引效果. 关于左右滑动效果,我在以前的一篇博文中提到过,有兴趣的朋友可以查看:http://www.cnblogs.com/hanyonglu/archive/ ...
- [Android] Android 类似今日头条顶部的TabLayout 滑动标签栏 效果
APP市场中大多数新闻App都有导航菜单,导航菜单是一组标签的集合,在新闻客户端中,每个标签标示一个新闻类别,对应下面ViewPager控件的一个分页面,今日头条, 网易新闻等. 本文主要讲的是用:T ...
- 原生H5页面模拟APP左侧滑动删除效果
话不多说,往左侧滑动,显示删除,我们先来看一下效果图:如下: 这个布局我就不多说,反正就是一行ul,li, class名“item” js代码如下: $(".item").on(& ...
随机推荐
- C# 64位系统中类型所占空间大小
Boolean 8Byte DateTime 8Byte Decimal 16Byte String 引用地址空间8Bypte Int 4Bypte 类所占空间大小 (byte):各个filed ...
- centos apache安装和设置
分类: LINUX 安装方式:yum install httpdyum install mysql-serveryum install phpyum install php-mysql 一.WEB服务 ...
- Surround the Trees---hdu1392(凸包GraHam模板)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1392 题意:有n棵树,每棵树有一个坐标,想用一些绳子把这些树包含起来,求需要绳子的长度: 就是求凸包的 ...
- JS之Array.slice()方法
1.Array.slice(startIndex,endIndex); 返回由原始数组从startIndex到endIndex-1的元素构成的新数组; startIndex:默认值0,如果startI ...
- JavaScript:画廊案例
经常会在网上看到这样的操作:有几个按钮.可以控制器图片的上一张.下一张.那么现在就使用按钮的click单击事件来切换img的图片. 此时由于只是简单的演示,将所有的图片保存在images目录中.所有的 ...
- MySQL学习笔记——复制的实现原理
1.三个线程 MYSQL复制是从主服务器复制到一个或多个从服务器的异步过程,在主服务器与从服务器之间实现整个复制过程主要由三个线程来实现,其中一个线程I\O在主服务器器端,另两个线程(SQL线程和I\ ...
- 8款实用Sublime text 3插件推荐
Sublime Text作为一个尽为人知的代码编辑器,其优点不用赘述.界面整洁美观.文本功能强大,且运行速度极快,非常适合编写代码,写文章做笔记.Sublime Text还支持Mac.Windows和 ...
- vsftp 定时任务同步
yum install db4-utils.x86_64 -y yum install ftp vsftp lftp 在客户端 和服务端都安装vsftp服务 并配置虚拟账号 上传包内文件 [root@ ...
- Python基础 (yield生成器)
如果在一个函数中使用了yield,那么这个函数实际上生成的是一个生成器函数 ,返回的是一个generator object.生成器是实现迭代的一种方式 特点: 其实返回的就是可以的迭代对象 和迭代的方 ...
- gcc选项-g与-rdynamic的异同_转
转自:http://www.tuicool.com/articles/EvIzUn gcc 的 -g ,应该没有人不知道它是一个调试选项,因此在一般需要进行程序调试的场景下,我们都会加上该选项,并且根 ...