Android中View的炸裂特效实现方法详解

Android中View的炸裂特效实现方法详解,第1张

概述本文实例讲述了Android中View的炸裂特效实现方法。分享给大家供大家参考,具体如下:

本文实例讲述了AndroID中VIEw的炸裂特效实现方法。分享给大家供大家参考,具体如下:

前几天微博上被一个很优秀的 AndroID 开源组件刷屏了 - ExplosionFIEld,效果非常酷炫,有点类似 Miui 卸载 APP 时的动画,先来感受一下。

ExplosionFIEld 不但效果很拉风,代码写得也相当好,让人忍不住要拿来好好读一下。

创建 ExplosionFIEld

ExplosionFIEld 继承自 VIEw,在 onDraw 方法中绘制动画特效,并且它提供了一个 attach2Window 方法,可以把 ExplosionFIEld 最为一个子 VIEw 添加到 Activity 上的 root vIEw 中。

public static ExplosionFIEld attach2Window(Activity activity) { VIEwGroup rootVIEw = (VIEwGroup) activity.findVIEwByID(Window.ID_ANDROID_CONTENT); ExplosionFIEld explosionFIEld = new ExplosionFIEld(activity); rootVIEw.addVIEw(explosionFIEld,new VIEwGroup.LayoutParams(   VIEwGroup.LayoutParams.MATCH_PARENT,VIEwGroup.LayoutParams.MATCH_PARENT)); return explosionFIEld;}

explosionFIEld 的 LayoutParams 属性都被设置为 MATCH_PARENT,
这样一来,一个 vIEw 炸裂出来的粒子可以绘制在整个 Activity 所在的区域。

知识点:可以用 Window.ID_ANDROID_CONTENT 来替代 androID.R.ID.content

炸裂之前的震动效果

在 VIEw 的点击事件中,调用 mExplosionFIEld.explode(v)之后,VIEw 首先会震动,然后再炸裂。

震动效果比较简单,设定一个 [0,1] 区间 ValueAnimator,然后在 AnimatorUpdateListener 的 onAnimationUpdate 中随机平移 x 和 y坐标,最后把 scale 和 Alpha 值动态减为 0。

int startDelay = 100;ValueAnimator animator = ValueAnimator.offloat(0f,1f).setDuration(150);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { Random random = new Random(); @OverrIDe public voID onAnimationUpdate(ValueAnimator animation) {  vIEw.setTranslationX((random.nextfloat() - 0.5f) * vIEw.getWIDth() * 0.05f);  vIEw.setTranslationY((random.nextfloat() - 0.5f) * vIEw.getHeight() * 0.05f); }});animator.start();vIEw.animate().setDuration(150).setStartDelay(startDelay).scaleX(0f).scaleY(0f).Alpha(0f).start();

根据 VIEw 创建一个 bitmap

VIEw 震动完了就开始进行最难的炸裂,并且炸裂是跟隐藏同时进行的,先来看一下炸裂的 API -

voID explode(Bitmap bitmap,Rect bound,long startDelay,long duration)

前两个参数 bitmap 和 bound 是关键,通过 VIEw 来创建 bitmap 的代码比较有意思。

如果 VIEw 是一个 ImageVIEw,并且它的 Drawable 是一个 BitmapDrawable 就可以直接获取这个 Bitmap。

if (vIEw instanceof ImageVIEw) { Drawable drawable = ((ImageVIEw) vIEw).getDrawable(); if (drawable != null && drawable instanceof BitmapDrawable) {  return ((BitmapDrawable) drawable).getBitmap(); }}

如果不是一个 ImageVIEw,可以按照如下步骤创建一个 bitmap:

1. 新建一个 Canvas

2. 根据 VIEw 的大小创建一个空的 bitmap

3. 把空的 bitmap 设置为 Canvas 的底布

4. 把 vIEw 绘制在 canvas上

5. 把 canvas 的 bitmap 设置成 null

当然,绘制之前要清掉 VIEw 的焦点,因为焦点可能会改变一个 VIEw 的 UI 状态。

以下代码中用到的 sCanvas 是一个静态变量,这样可以节省每次创建时产生的开销。

vIEw.clearFocus();Bitmap bitmap = createBitmapSafely(vIEw.getWIDth(),vIEw.getHeight(),Bitmap.Config.ARGB_8888,1);if (bitmap != null) { synchronized (sCanvas) {  Canvas canvas = sCanvas;  canvas.setBitmap(bitmap);  vIEw.draw(canvas);  canvas.setBitmap(null); }}

作者创建位图的办法非常巧妙,如果新建 Bitmap 时产生了 OOM,可以主动进行一次 GC - System.gc(),然后再次尝试创建。

这个函数的实现方式让人佩服作者的功力。

public static Bitmap createBitmapSafely(int wIDth,int height,Bitmap.Config config,int retryCount) { try {  return Bitmap.createBitmap(wIDth,height,config); } catch (OutOfMemoryError e) {  e.printstacktrace();  if (retryCount > 0) {   System.gc();   return createBitmapSafely(wIDth,config,retryCount - 1);  }  return null; }}

出了 bitmap,还有一个一个很重要的参数 bound,它的创建相对比较简单:

Rect r = new Rect();vIEw.getGlobalVisibleRect(r);int[] location = new int[2];getLocationOnScreen(location);r.offset(-location[0],-location[1]);r.inset(-mExpandInset[0],-mExpandInset[1]);

首先获取 需要炸裂的VIEw 的全局可视区域 - Rect r,然后通过 getLocationOnScreen(location) 获取 ExplosionFIEld 在屏幕中的坐标,并根据这个坐标把 炸裂VIEw 的可视区域进行平移,这样炸裂效果才会显示在 ExplosionFIEld 中,最后根据 mExpandInset 值(默认为 0)扩展一下。

那创建的 bitmap 和 bound 有什么用呢?我们继续往下分析。

创建粒子

先来看一下炸裂成粒子这个方法的全貌:

public voID explode(Bitmap bitmap,long duration) { final ExplosionAnimator explosion = new ExplosionAnimator(this,bitmap,bound); explosion.addListener(new AnimatorListenerAdapter() {  @OverrIDe  public voID onAnimationEnd(Animator animation) {   mExplosions.remove(animation);  } }); explosion.setStartDelay(startDelay); explosion.setDuration(duration); mExplosions.add(explosion); explosion.start();}

这里要解释一下为什么用一个容器类变量 - mExplosions 来保存一个 ExplosionAnimator。因为 activity 中多个 VIEw 的炸裂效果可能要同时进行,所以要把每个 VIEw 对应的炸裂动画保存起来,等动画结束的时候再删掉。

作者自定义了一个继承自 ValueAnimator 的类 - ExplosionAnimator,它主要做了两件事情,一个是创建粒子 - generateParticle,另一个是绘制粒子 - draw(Canvas canvas)。

先来看一下构造函数:

public ExplosionAnimator(VIEw container,Bitmap bitmap,Rect bound) { mPaint = new Paint(); mBound = new Rect(bound); int partLen = 15; mParticles = new Particle[partLen * partLen]; Random random = new Random(System.currentTimeMillis()); int w = bitmap.getWIDth() / (partLen + 2); int h = bitmap.getHeight() / (partLen + 2); for (int i = 0; i < partLen; i++) {  for (int j = 0; j < partLen; j++) {   mParticles[(i * partLen) + j] = generateParticle(bitmap.getPixel((j + 1) * w,(i + 1) * h),random);  } } mContainer = container; setfloatValues(0f,END_VALUE); setInterpolator(DEFAulT_INTERPolATOR); setDuration(DEFAulT_DURATION);}

根据构造函数可以知道作者把 bitmap 分成了一个 17 x 17 的矩阵,每个元素的宽度和高度分别是 w 和 h。

int w = bitmap.getWIDth() / (partLen + 2);int h = bitmap.getHeight() / (partLen + 2);

所有的粒子是一个 15 x 15 的矩阵,元素色值是位图对应的像素值。

bitmap.getPixel((j + 1) * w,(i + 1) * h)

结构如下图所示,其中空心部分是粒子。

 ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ●

generateParticle 会根据一定的算法随机地生成一个粒子。这部分比较繁琐,分析略去。

其中比较巧妙的还是它的 draw 方法:

public boolean draw(Canvas canvas) { if (!isstarted()) {  return false; } for (Particle particle : mParticles) {  particle.advance((float) getAnimatedValue());  if (particle.Alpha > 0f) {   mPaint.setcolor(particle.color);   mPaint.setAlpha((int) (color.Alpha(particle.color) * particle.Alpha));   canvas.drawCircle(particle.cx,particle.cy,particle.radius,mPaint);  } } mContainer.invalIDate(); return true;}

刚开始我还一直比较困惑,既然绘制粒子是在 ExplosionFIEld 的 onDraw 方法中进行,那肯定需要不停地刷新,结果作者并不是这么做的,实现方法又着实惊艳了一把。

首先,作者在 ExplosionAnimator 类中重载了 start() 方法,通过调用 mContainer.invalIDate(mBound) 来刷新 将要炸裂的 VIEw 所对应的区块。

@OverrIDepublic voID start() { super.start(); mContainer.invalIDate(mBound);}

而 mContainer 即是占满了 activity 的 vIEw - ExplosionFIEld,它的 onDraw 方法中又会调用 ExplosionAnimator 的 draw 方法。

@OverrIDeprotected voID onDraw(Canvas canvas) { super.onDraw(canvas); for (ExplosionAnimator explosion : mExplosions) {  explosion.draw(canvas); }}

这样便形成了一个递归,两者相互调用,不停地刷新,直到所有粒子的 Alpha 值变为 0,刷新就停下来了。

public boolean draw(Canvas canvas) { if (!isstarted()) {  return false; } for (Particle particle : mParticles) {  particle.advance((float) getAnimatedValue());  if (particle.Alpha > 0f) {   mPaint.setcolor(particle.color);   mPaint.setAlpha((int) (color.Alpha(particle.color) * particle.Alpha));   canvas.drawCircle(particle.cx,mPaint);  } } mContainer.invalIDate(); return true;}

总结

这个开源库的代码质量相当高,十分佩服作者。

更多关于AndroID相关内容感兴趣的读者可查看本站专题:《Android视图View技巧总结》、《Android *** 作XML数据技巧总结》、《Android编程之activity *** 作技巧总结》、《Android资源 *** 作技巧汇总》、《Android文件 *** 作技巧汇总》、《Android *** 作SQLite数据库技巧总结》、《Android *** 作json格式数据技巧总结》、《Android数据库 *** 作技巧总结》、《Android编程开发之SD卡 *** 作方法汇总》、《Android开发入门与进阶教程》及《Android控件用法总结》

希望本文所述对大家AndroID程序设计有所帮助。

总结

以上是内存溢出为你收集整理的Android中View的炸裂特效实现方法详解全部内容,希望文章能够帮你解决Android中View的炸裂特效实现方法详解所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1148772.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存