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(& ...
随机推荐
- docker confluence
http://wuyijun.cn/shi-yong-dockerfang-shi-an-zhuang-he-yun-xing-confluence/ https://hub.docker.com/r ...
- CSS中position属性 (absolute,relative,static,fixed)
只要position的属性值设置的不是默认的值则定位的元素都将脱离文档流 1.static是position的默认的值,按照正常的文档流进行排版,设置了该属性值得元素的top,left属性均不起作用. ...
- Qt的学习资料比起其它C/C++的GUI组件来说已经算很全的了
Qt的学习资料比起其它C/C++的GUI组件来说已经算很全的了.Google的话能解决很多问题,如果没搜到资料的话,如果不是问题太过具体或者奇葩,那就是搜索方法的问题.中文教程中,Qt学习之路系列很不 ...
- 强调语气<strong>和<em>标签,文字设置单独样式<span>
区别:1,<em> 表示强调,<strong> 表示更强烈的强调. 2,并且在浏览器中<em> 默认用斜体表示,<strong> 用粗体表示. 3,两个 ...
- ArcGIS三大文件格式解析
原文:ArcGIS三大文件格式解析 Shape数据 Shapefile是ArcView GIS 3.x的原生数据格式,属于简单要素类,用点.线.多边形存储要素的形状,却不能存储拓扑关系,具有简单.快速 ...
- php保存远程文件到本地的方法
用到了ob_start();<?php header("Content-type:text/html charset=utf-8"); if(!empty($_POST['p ...
- 使用jQuery为表单添加回车事件
$(document).keypress(function(e){ if(e.which==13){ checkUserForm(); } });
- zabbix监控MySQL
通过使用mysql_performance_monitor软件包实现zabbix对mysql的监控. 1.安装依赖软件.yum install perl-File-Which perl-libwww- ...
- Linux命令行--使用linux环境变量(转)
5.1 什么时环境变量 bash shell用一个成为环境变量的特性来存储有关的shell回话和工作环境的信息,这是它们成为环境变量的原因.它允许你在内存中存储数据,以便运行在账户.系统.shell的 ...
- PHP如何解决网站大流量与高并发的问题
首先,确认服务器硬件是否足够支持当前的流量. 普通的P4服务器一般最多能支持每天10万独立IP,如果访问量比这个还要大, 那么必须首先配置一台更高性能的专用服务器才能解决问题 ,否则怎么优化都不可能彻 ...