android 破碎酷炫动画,【Android效果集】学习ExplosionField之粒子破碎效果
编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识、前端、后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过!
前段时间在某效果网站看到开源项目【ExplosionField】非常喜欢,于是自己跟着源码学习着去做了做。跟源码效果有一点区别,我都是尽力读懂源码然后用自己的理解写出来,源码有些看不懂的地方,我也就没有用到,因为自己的代码要保证自己都能看懂。
最后效果如下:
(本文适合有一年Android开发经验者学习)
本文可以学到: 1.开源项目ExplosionField的实现思路 2.图示效果的实现过程 3.属性动画的用法
实现思路:
1.新建一个 Bean Particle,表示一个粒子对象;新建一个 View ExplosionField作为画布用来显示破碎的粒子;新建一个属性动画(ValueAnimator) ExplosionAnimator用来改变不同时刻的粒子状态;
2.通过View生成图片Bitmap,把生成的图片分解成若干个粒子,让每个粒子记录特定的位置,所有的粒子组合能看出是原图。
3.加上动画效果,使得点击View后,粒子能有所变化。
4.构思算法,形成不一样的效果。
5.匹配不同分辨率的设备。
6.重构。
详细过程:
可以先看看项目结构,非常简单:
v v
1.新建对象
1.1 新建Particle对象,用来描述粒子,包括属性有颜色、透明度、圆心坐标、半径。public class Particle {
float cx; //center x of circle
float cy; //center y of circle
float radius;
int color;
float alpha;
}
1.2 新建ExplosionField对象,继承自View,用于做粒子集的画布,需要重写onDraw()方法public class ExplosionField extends View{
public ExplosionField(Context context) {
super(context);
init();
}
public ExplosionField(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
//初始化
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制粒子
}
}
1.3 新建ExplosionAnimator,继承自ValueAnimator,用来执行自定义动画。ValueAnimator简单来说就是在一段时间内通过不断改变值(一般是改变某个属性的值)来达到动画效果。更多可以参考《Android属性动画完全解析(上),初识属性动画的基本用法》来学习。
而我们现在是准备在一段时间内(大概1.5秒)让ValueAnimator里的值从0.0f变化到1.0f,然后根据系统生成的递增随机值(范围在0.0f~1.0f)改变Particle里的属性值。public class ExplosionAnimator extends ValueAnimator{
public static final int DEFAULT_DURATION = 1500;
public ExplosionAnimator() {
setFloatValues(0.0f, 1.0f);
setDuration(DEFAULT_DURATION);
}
}
这样,在1.5秒内,通过ExplosionAnimator的方法getAnimatedValue()就能够不断得到递增的范围在0.0f~1.0f之间的值。
2.复制出View的快照图片
首先通过view的宽高创建出一个同样大小的空白图,用Bitmap的静态方法createBitmap()创建,最后一个参数表示图片质量。Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
然后通过画布Canvas,先把空白图设置到画布里,再让view把自己画在画布上,空白图也变成了view的翻版了。mCanvas.setBitmap(bitmap);
view.draw(mCanvas);
//此处bitmap已是同view显示一样的图
完整代码://ExplosionField.java
public class ExplosionField extends View{
private static final Canvas mCanvas = new Canvas();
private Bitmap createBitmapFromView(View view) {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
if (bitmap != null) {
synchronized (mCanvas) {
mCanvas.setBitmap(bitmap);
view.draw(mCanvas);
mCanvas.setBitmap(null); //清除引用
}
}
return bitmap;
}
}
PS:在原项目ExplosionField中还有一个判断,如果view是ImageView的对象,那么直接获得ImageView依附的BitmapDrawable图。if (view instanceof ImageView) {
Drawable drawable = ((ImageView)view).getDrawable();
if (drawable != null && drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
}
我为什么去掉了呢?是因为如果ImageView设置了背景(background)的话,这样直接获取的BitmapDrawable是src的引用,并不包括背景色。所以统一用画布绘制的方法生成快照。
好了,先拿一个TextView做示范,看看复制的效果:
3.把快照分解成若干粒子
前面我们已经生成了快照图片,现在我们需要把快照分解成若干个粒子,这些粒子的组合能看出来是原图的影子,然后再让粒子动起来形成后面的动画。
那怎么做呢?ExplosionField项目是分解成15 * 15个粒子,我这里有点不一样我就直接按照我的思路讲解了。
首先定义一个二维数组Particle[][](一维的也行啦,原项目就是定义一维的),用来存放所有粒子,因为图片大小不同,粒子个数也不会相同,所以我们把粒子的宽高固定,在Particle类中新加一个静态常量属性public static final int PART_WH = 8; //默认小球宽高
然后根据view的宽高,算出横竖粒子的个数//ExplosionAnimator.java - generateParticles(Bitmap bitmap, Rect bound)
int w = bound.width();
int h = bound.height();
int partW_Count = w / Particle.PART_WH; //横向个数
int partH_Count = h / Particle.PART_WH; //竖向个数
Particle[][] particles = new Particle[partH_Count][partW_Count];
其中bound是Rect类型,通过view.getGlobalVisibleRect()方法能得到view相对于整个屏幕的坐标Rect bound = new Rect();
view.getGlobalVisibleRect(rect);
然后把二维粒子数组对应图片的位置,设置为相应的颜色属性和坐标。
通过bitmap.getPixel(x, y)可以获得(x, y)坐标的bitmap的颜色值//ExplosionAnimator.java - generateParticles(Bitmap bitmap, Rect bound)
Point point = null;
for (int row = 0; row
for (int column = 0; column
//取得当前粒子所在位置的颜色
int color = bitmap.getPixel(column * partW_Count, row * partH_Count);
point = new Point(column, row); //x是列,y是行
particles[row][column] = Particle.generateParticle(color, bound, point);
}
}
在Particle类中定义静态方法generateParticle()用来生成新的Particle对象//Particle.java
public static Particle generateParticle(int color, Rect bound, Point point) {
int row = point.y; //行是高
int column = point.x; //列是宽
Particle particle = new Particle();
particle.mBound = bound;
particle.color = color;
particle.alpha = 1f;
particle.radius = PART_WH;
particle.cx = bound.left + PART_WH * column;
particle.cy = bound.top + PART_WH * row;
return particle;
}
这里把半径设置为宽长,而不是宽的一半,是因为叠加显示效果会更好看一点。
为了能够显示出来,我们新建一个draw()方法,用从ExplosionField传来的canvas来绘制所有粒子//ExplosionAnimator.java
public void draw(Canvas canvas) {
for (Particle[] particle : mParticles) {
for (Particle p : particle) {
canvas.drawCircle(p.cx, p.cy, p.radius, mPaint);
}
}
}
//ExplosionField.java
private ArrayList explosionAnimators;
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (ExplosionAnimator animator : explosionAnimators) {
animator.draw(canvas);
}
}
因为画布可能同时绘制几个动画,所以用一个List保存动画集。
现在大概的效果是这样:
4.加上动画,使得粒子动起来
前面说过,在ExplosionAnimator中通过方法getAnimatedValue()就能够不断得到递增的范围在0.0f~1.0f之间的值(记做factor)。
我们先在Particle写好得到变化因素后,属性要发生的改变。cx左右移动都可以,cy向下移动且距离和view高度有关(不同高度图片,每次下降距离不同),radius变小,alpha变得越来越透明。只要符合这几点,算法随便写就可以了。//Particle.java
public void advance(float factor) {
cx = cx + factor * random.nextInt(mBound.width()) * (random.nextFloat() - 0.5f);
cy = cy + factor * random.nextInt(mBound.height() / 2);
radius = radius - factor * random.nextInt(2);
alpha = (1f - factor) * (1 + random.nextFloat());
}
记住传进来的factor是从0.0f到1.0f不断递增的。
然后改造draw()方法,每次绘制都让粒子“前进一步”调用一次advance()方法,然后根据新属性重新绘制//ExplosionAnimator.java
public void draw(Canvas canvas) {
if(!isStarted()) { //动画结束时停止
return;
}
for (Particle[] particle : mParticles) {
for (Particle p : particle) {
p.advance((Float) getAnimatedValue());
mPaint.setColor(p.color);
mPaint.setAlpha((int) (Color.alpha(p.color) * p.alpha)); //这样透明颜色就不是黑色了
canvas.drawCircle(p.cx, p.cy, p.radius, mPaint);
}
}
mContainer.invalidate();
}
最后一句的mContainer其实就是ExplosionField,调用它的invalidate()方法,就是调用ExplosionField的onDraw()方法。而ExplosionField的onDraw()里又调用了ExplosionAnimator的draw()方法。这样循环就出现了动画效果。
结束的条件就是第一句if(!isStarted())如果动画停止了,就断了绘制循环。
PS:这里值得一提的有setAlpha()方法,之前我用的是mPaint.setColor(p.color);
mPaint.setAlpha((int) (255 * p.alpha));
这样有个问题就是当颜色为透明时,显示的是黑色。
而改为了方法:mPaint.setColor(p.color);
mPaint.setAlpha((int) (Color.alpha(p.color) * p.alpha));
透明颜色就为透明色了。
现在动画过程已经写完,就差开始的导火线了,我们在动画开始的时候启动这根导火线,重写start()方法://ExplosionAnimator.java
@Override
public void start() {
super.start();
mContainer.invalidate();
}
那在哪使动画开始呢,即在哪调用explosionAnimator.start()呢?
在ExplosionField中建立一个“爆炸”方法,只要调用这个方法,传入view,最后执行animator.start(),view就会执行爆炸效果public void explode(final View view) {
Rect rect = new Rect();
view.getGlobalVisibleRect(rect); //得到view相对于整个屏幕的坐标
rect.offset(0, -Utils.dp2px(25)); //去掉状态栏高度
final ExplosionAnimator animator = new ExplosionAnimator(this, createBitmapFromView(view), rect);
explosionAnimators.add(animator);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
view.animate().alpha(0f).setDuration(150).start();
}
@Override
public void onAnimationEnd(Animator animation) {
view.animate().alpha(1f).setDuration(150).start();
//动画结束时从动画集中移除
explosionAnimators.remove(animation);
animation = null;
}
});
animator.start();
}
现在的效果:
5.附着动画到任意Activity,添加监听器给需要有动画效果的view
现在动画效果什么的都做好了,要如何使用呢?
现在的思路是在Activity的最上层盖一层透明的ExplosionField视图,用来显示粒子动画。//ExplosionField.java
/**
* 给Activity加上全屏覆盖的ExplosionField
*/
private void attach2Activity(Activity activity) {
ViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
rootView.addView(this, lp);
}
其实Activity的根视图并不是我们设置的xml,它上面还有一层,通过findViewById(Window.ID_ANDROID_CONTENT)能够得到,然后我们再把ExplosionField全屏加载在Activity的最上层,这样显示动画效果就不会被遮盖。
然后我们可以在初始化的时候加上这个方法:public class ExplosionField extends View{
public ExplosionField(Context context) {
super(context);
init();
}
public ExplosionField(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
...
attach2Activity((Activity) getContext());
}
...
}
在看Activity的onCreate()方法就非常简单了://MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_az);
ExplosionField explosionField = new ExplosionField(this);
explosionField.addListener(findViewById(R.id.root));
}
最后一句调用了addListener()方法,就是把需要实现点击破碎效果的view加上监听器,看代码:public void addListener(View view) {
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
int count = viewGroup.getChildCount();
for (int i = 0 ; i
addListener(viewGroup.getChildAt(i));
}
} else {
view.setClickable(true);
view.setOnClickListener(getOnClickListener());
}
}
private OnClickListener getOnClickListener() {
if (null == onClickListener) {
onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
ExplosionField.this.explode(v);
}
};
}
return onClickListener;
}
只要传入ViewGroup,会自动递归查找Child View,并给Child View加上点击监听器,一旦点击就调用爆破方法执行动画。
最终效果大图:
更多详细代码可 fork 源码查看!
如果你喜欢这个效果,请给我Github上一个Star鼓励一下哈O(∩_∩)O谢谢!
android 破碎酷炫动画,【Android效果集】学习ExplosionField之粒子破碎效果相关推荐
- android 破碎酷炫动画,Android特效——玻璃破碎效果
玻璃破碎效果.你们还是看效果图吧,挺不错的,动手能力强,啥都能实现!贴一小段源码,工程末尾下载,是用Android Studio开发的. BrokenView.java import android. ...
- java水果爆炸动画_5分钟快速实现Android爆炸破碎酷炫动画特效的示例
这个破碎动画,是一种类似小米系统删除应用时的爆炸破碎效果的动画. 效果图展示 先来看下是怎样的动效,要是感觉不是理想的学习目标,就跳过,避免浪费大家的时间.�� 一行代码即可调用该动画 new Exp ...
- 学习ExplosionField之粒子破碎效果
(本文适合有一年Android开发经验者学习) 本文可以学到: 1.开源项目ExplosionField的实现思路 2.图示效果的实现过程 3.属性动画的用法 实现思路: 1.新建一个 Bean ...
- Android矢量图动画特效,Android使用SVG矢量图打造酷炫动画效果
一个真正Android使用SVG矢量图打造酷炫动效往往让人虎躯一震,话不多说,咱们先看看效果: 这个效果我们需要考虑以下几个问题: 1. 这是图片还是文字: 2. 如果是图片该如何拿到图形的边沿线坐标 ...
- Android kotlin实现Recyclerview酷炫动画
在现在进行app开发中 针对app的UI 美观效果要求越来越高了 所以开发出一款酷炫的app相当重要了 今天就对RecyclerView来实现酷炫动画 首先在Android studio中 的项目中 ...
- Android常用酷炫控件(开源项目)github地址汇总
转载一个很牛逼的控件收集帖... 第一部分 个性化控件(View) 主要介绍那些不错个性化的 View,包括 ListView.ActionBar.Menu.ViewPager.Gallery.Gri ...
- 酷炫的Android开源项目
抽屉菜单 MaterialDrawer ★7337 - 安卓抽屉效果实现方案 Side-Menu.Android ★3865 - 创意边侧菜单 FlowingDrawer ★1744 - 向右滑动流动 ...
- android 酷炫编辑框_25个实用酷炫的Android开源UI框架
最近找了一些合适开源控件,这样在日常工作中会更加省时,再此分享给大家,希望能对大家有帮助,此博文介绍的都是UI上面的框架,接下来会有其他的开源框架(如:HTTP框架.DB框架). 1.Side-Men ...
- 自定义动画属性java_创建酷炫动画效果的10个JavaScript库
原标题:创建酷炫动画效果的10个JavaScript库 1) Dynamics.jsDynamics.js是设计基于物理规律的动画的重要Java库.它可以赋予生命给所有包含CSS 和SVG属性的DOM ...
最新文章
- 删除链表中全部值为k的节点
- 【 C 】结构体 与 typedef
- Django 开发中的最佳实践之一
- eclipse 快捷键汇总
- 文件分享平台php源码,简易社会化用户文件分享系统 v1.0
- Windows上传代码到github操作指导
- MYSQL_使用外键约束(constraint)或触发器(trigger)来进行级联更新、删除
- 中国电信计划构建安全新平台
- DE标识DellUtility磁盘手动创建
- 《深入Ajax架构和最佳实践》读书笔记
- 170925_Spring Cloud 微服务实战(翟永超著) 读书笔记(二)_什么是Spring Cloud Eureka?
- 为选区添加描边_ps怎么给选区加上虚线描边
- 派生类的构造函数xu(c++)
- python采集keep运动数据
- Unity - 人物对象的 LOD 管理
- 微信苹果多开系统官网下载页源码
- 七牛图片上传的使用心得[PHP篇]
- Java图片相似度,图像识别
- 活久见,Windows系统源码竟然真的泄露了!
- 自适应波束形成(二)——时域窄带LCMV波束形成器
热门文章
- 富文本内图片大小调整,判断有无图片,限制图片大小
- CSS盒子模型/PS基操/圆角边框/盒子阴影/文字阴影/案例
- php时间戳 剩余日期时间_PHP中UNIX时间戳和日期间的转换与计算实例
- echarts3中国地图学习理解马克
- 博菱电器创业板过会:收入依赖单一客户,“创二代”袁琪本科肄业
- iOS企业发布问题记录一(为什么iOS的应用安装到一半时提示无法下载应用程序?)
- 福昕阅读器怎么添加电子图章?
- Android 12 关机重启流程
- 语音助理究竟好不好用?看看国外消费者怎么说 | 精选
- poi-tl导出word实现图片环绕方式为浮于在文字上方办法