关闭

详解ExplosionField的使用,实现View的粉碎效果

105人阅读 评论(0) 收藏 举报


目录(?)[+]

小米平板卸载软件的时候,会有一个粉碎的效果,看起来很拉风,GitHub上有一个开源控件可以实现这个效果,我们一起来看看。先来看看效果图:


看起来不错吧,那我们今天就来详细说说ExplosionField的使用和它内部的实现原理吧。

1.获得一个ExplosionField

ExplosionField是GitHub上的一个开源项目,我们直接在GitHub上下载就可以了。

下载地址https://github.com/tyrantgit/ExplosionField

下载好之后我们直接将tyrantgit文件夹拷贝到我们的项目中就可以使用了,该文件夹的位置在:ExplosionField-master\ExplosionField-master\explosionfield\src\main\java中。

注意:使用ExplosionField要求我们的jre版本在1.7以上。


2.ExplosionField的使用

布局文件很简单,就是四个ImageView:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <LinearLayout  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_weight="1"  
  11.         android:gravity="center"  
  12.         android:orientation="horizontal" >  
  13.   
  14.         <ImageView  
  15.             android:id="@+id/iv1"  
  16.             android:layout_width="72dp"  
  17.             android:layout_height="72dp"  
  18.             android:layout_weight="1"  
  19.             android:onClick="onClick"  
  20.             android:src="@drawable/p1" />  
  21.   
  22.         <ImageView  
  23.             android:id="@+id/iv2"  
  24.             android:layout_width="72dp"  
  25.             android:layout_height="72dp"  
  26.             android:layout_weight="1"  
  27.             android:onClick="onClick"  
  28.             android:src="@drawable/p2" />  
  29.     </LinearLayout>  
  30.   
  31.   
  32.     <LinearLayout  
  33.         android:layout_width="match_parent"  
  34.         android:layout_height="wrap_content"  
  35.         android:layout_weight="1"  
  36.         android:orientation="horizontal" >  
  37.   
  38.         <ImageView  
  39.             android:id="@+id/iv3"  
  40.             android:layout_width="72dp"  
  41.             android:layout_height="72dp"  
  42.             android:layout_weight="1"  
  43.             android:onClick="onClick"  
  44.             android:src="@drawable/p3" />  
  45.   
  46.         <ImageView  
  47.             android:id="@+id/iv4"  
  48.             android:layout_width="72dp"  
  49.             android:layout_height="72dp"  
  50.             android:layout_weight="1"  
  51.             android:onClick="onClick"  
  52.             android:src="@drawable/p4" />  
  53.     </LinearLayout>  
  54.   
  55. </LinearLayout>  

在MainActivity中,我们拿到一个ExplosionField的实例,然后通过执行它的explode方法就可以实现View的粉碎效果了。

  1. public class MainActivity extends Activity {  
  2.   
  3.     private ExplosionField explosionField;  
  4.     private ImageView iv1, iv2, iv3, iv4;  
  5.   
  6.     @Override  
  7.     protected void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.         setContentView(R.layout.activity_main);  
  10.         iv1 = (ImageView) this.findViewById(R.id.iv1);  
  11.         iv2 = (ImageView) this.findViewById(R.id.iv2);  
  12.         iv3 = (ImageView) this.findViewById(R.id.iv3);  
  13.         iv4 = (ImageView) this.findViewById(R.id.iv4);  
  14.         explosionField = ExplosionField.attach2Window(this);  
  15.     }  
  16.   
  17.     public void onClick(View v) {  
  18.         switch (v.getId()) {  
  19.         case R.id.iv1:  
  20.             explosionField.explode(iv1);  
  21.             iv1.setVisibility(View.INVISIBLE);  
  22.             break;  
  23.         case R.id.iv2:  
  24.             explosionField.explode(iv2);  
  25.             iv2.setVisibility(View.INVISIBLE);  
  26.             break;  
  27.         case R.id.iv3:  
  28.             explosionField.explode(iv3);  
  29.             iv3.setVisibility(View.INVISIBLE);  
  30.             break;  
  31.         case R.id.iv4:  
  32.             explosionField.explode(iv4);  
  33.             iv4.setVisibility(View.INVISIBLE);  
  34.             break;  
  35.         }  
  36.     }  
  37. }  

使用起来是如此的方便,这里我要特别说一句,为什么粉碎完成之后还要将控件隐藏,这是因为View粉碎之后,我们就看不到了,但是实际上它还在页面上,如果这个View本身有点击事件,那么这个时候你点击它粉碎前的位置,点击事件还是能够响应,将控件隐藏之后就可以避免这个问题了。说了这么多,我们来看看这个效果究竟是怎么实现的。


3.源码解读

通过前面两步,我们已经知道,跟这个效果有关的就是三个类,那么我们这里就从explosionField = ExplosionField.attach2Window(this);这行代码所执行的attach2Window(this);方法看起:

  1. public static ExplosionField attach2Window(Activity activity) {  
  2.     ViewGroup rootView = (ViewGroup) activity  
  3.             .findViewById(Window.ID_ANDROID_CONTENT);  
  4.     ExplosionField explosionField = new ExplosionField(activity);  
  5.     rootView.addView(explosionField, new ViewGroup.LayoutParams(  
  6.             ViewGroup.LayoutParams.MATCH_PARENT,  
  7.             ViewGroup.LayoutParams.MATCH_PARENT));  
  8.     return explosionField;  
  9. }  

首先我们要知道,ExplosionField继承自View,它本身就是一个View,那么在attach2Window方法中,我们先拿到一个rootView,这个rootView其实就是我们setContentView(R.layout.activity_main);所设置的View,然后我们new一个ExplosionField并把它添加到rootView当中,并把它的宽高都设置为MATCH_PARENT,这样就可以保证View爆炸产生的碎片可以被绘制在整个Activity区域。

好了,拿到一个ExplosionField对象之后,我们就可以粉碎一个View了,粉碎View执行的方法是explode,这个方法需要我们把要粉碎的View作为一个参数传入进去,我们看看这个方法:

  1. public void explode(final View view) {  
  2.     Rect r = new Rect();  
  3.     view.getGlobalVisibleRect(r);  
  4.     int[] location = new int[2];  
  5.     getLocationOnScreen(location);  
  6.     r.offset(-location[0], -location[1]);  
  7.     r.inset(-mExpandInset[0], -mExpandInset[1]);  
  8.     int startDelay = 100;  
  9.     ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150);  
  10.     animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  11.   
  12.         Random random = new Random();  
  13.   
  14.         @Override  
  15.         public void onAnimationUpdate(ValueAnimator animation) {  
  16.             view.setTranslationX((random.nextFloat() - 0.5f)  
  17.                     * view.getWidth() * 0.05f);  
  18.             view.setTranslationY((random.nextFloat() - 0.5f)  
  19.                     * view.getHeight() * 0.05f);  
  20.   
  21.         }  
  22.     });  
  23.     animator.start();  
  24.     view.animate().setDuration(150).setStartDelay(startDelay).scaleX(0f)  
  25.             .scaleY(0f).alpha(0f).start();  
  26.     explode(Utils.createBitmapFromView(view), r, startDelay,  
  27.             ExplosionAnimator.DEFAULT_DURATION);  
  28. }  
这里的代码我们可以分为4部分来理解:

第一部分:2-7行

这一段代码我们主要是用来获取要粉碎View的一个可视区域,拿到这个Rect之后,我们再拿到ExplosionField在当前页面中的坐标,然后根据这个坐标对Rect进行平移,平移之后就可以让粉碎的效果在ExplosionField中显示,最后我们还要将Rect的区域扩大一下,默认情况下,横向扩大64dp,纵向扩大64dp,mExpandInset的初始化在ExplosionField的构造方法中调用,最终拿到的Rect我们会在第四部分使用。

第二部分:8-23行

这一段代码主要是是完成粉碎前的振动效果,一个View要被粉碎了,吓得它先打个哆嗦。这里的实现还是比较简单的,先是生成一个随机数,根据这个随机数算出View在X轴和Y轴平移的距离,反复的平移就形成了振动效果。

第三部分:24-25行

这一段代码是将View隐藏,隐藏的方式就是让scaleX和scaleY、alpha最终都变为0f。

第四部分:26-27行

这一段代码是粉碎View的核心代码,我们下面分析。
好了,这一段代码算是一个准备工作,下面我们看看上面第四部分提到的这个explode的重载方法,这里一共需要四个参数,第一个参数是一个Bitmap,这个Bitmap根据我们传入的View创建,第二个参数就是我们上面第一部分提到的那个Rect,第三个参数是一个启动延迟时间,第四个参数是动画默认的执行时间,四个参数中,后面两个都是固定值,第二个参数我们上文已经说过,这里我们主要看看第一个参数的获得:

  1. public static Bitmap createBitmapFromView(View view) {  
  2.     if (view instanceof ImageView) {  
  3.         Drawable drawable = ((ImageView) view).getDrawable();  
  4.         if (drawable != null && drawable instanceof BitmapDrawable) {  
  5.             return ((BitmapDrawable) drawable).getBitmap();  
  6.         }  
  7.     }  
  8.     view.clearFocus();  
  9.     Bitmap bitmap = createBitmapSafely(view.getWidth(),  
  10.             view.getHeight(), Bitmap.Config.ARGB_8888, 1);  
  11.     if (bitmap != null) {  
  12.         synchronized (sCanvas) {  
  13.             Canvas canvas = sCanvas;  
  14.             canvas.setBitmap(bitmap);  
  15.             view.draw(canvas);  
  16.             canvas.setBitmap(null);  
  17.         }  
  18.     }  
  19.     return bitmap;  
  20. }  
  21.   
  22. public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) {  
  23.     try {  
  24.         return Bitmap.createBitmap(width, height, config);  
  25.     } catch (OutOfMemoryError e) {  
  26.         e.printStackTrace();  
  27.         if (retryCount > 0) {  
  28.             System.gc();  
  29.             return createBitmapSafely(width, height, config, retryCount - 1);  
  30.         }  
  31.         return null;  
  32.     }  
  33. }  

根据我们传入的View,我们得到一个Bitmap,如果这个View本身就是一个ImageView,那么我们会比较容易获得一个Bitmap,如果这个View不是ImageView,那么我们根据这个View的大小创建一个空的Bitmap,在创建的过程中,如果发生了OOM,我们会尝试执行一次System.gc,然后重新创建,如果还创建失败,那么就会返回一个null。拿到Bitmap之后,我们把这个Bitmap设置为一个Canvas的底布,然后把View绘制在Canvas上,最后再把Canvas的底布设置为null,同时将Bitmap返回。

经过上面的步骤,我们就可以拿到一个根据我们要粉碎的View创建的bitmap,然后我们就来看看explode的重载方法:

  1. public void explode(Bitmap bitmap, Rect bound, long startDelay,  
  2.         long duration) {  
  3.     final ExplosionAnimator explosion = new ExplosionAnimator(this, bitmap,  
  4.             bound);  
  5.     explosion.addListener(new AnimatorListenerAdapter() {  
  6.         @Override  
  7.         public void onAnimationEnd(Animator animation) {  
  8.             mExplosions.remove(animation);  
  9.         }  
  10.     });  
  11.     explosion.setStartDelay(startDelay);  
  12.     explosion.setDuration(duration);  
  13.     mExplosions.add(explosion);  
  14.     explosion.start();  
  15. }  
到了这里,我们先来看看ExplosionAnimator这个类,这个类继承自ValueAnimator,我们来看看ExplosionField的构造方法:
  1. public ExplosionAnimator(View container, Bitmap bitmap, Rect bound) {  
  2.     mPaint = new Paint();  
  3.     mBound = new Rect(bound);  
  4.     int partLen = 15;  
  5.     mParticles = new Particle[partLen * partLen];  
  6.     Random random = new Random(System.currentTimeMillis());  
  7.     int w = bitmap.getWidth() / (partLen + 2);  
  8.     int h = bitmap.getHeight() / (partLen + 2);  
  9.     for (int i = 0; i < partLen; i++) {  
  10.         for (int j = 0; j < partLen; j++) {  
  11.             mParticles[(i * partLen) + j] = generateParticle(  
  12.                     bitmap.getPixel((j + 1) * w, (i + 1) * h), random);  
  13.         }  
  14.     }  
  15.     mContainer = container;  
  16.     setFloatValues(0f, END_VALUE);  
  17.     setInterpolator(DEFAULT_INTERPOLATOR);  
  18.     setDuration(DEFAULT_DURATION);  
  19. }  
在这里我们看到了View被粉碎的方式,一个View一共被分为15×15个Particle,通过generateParticle方法可以获得这一个一个的Particle,每一个Particle的颜色就是bitmap上对应的颜色,这些Particle存在一个mParticles数组中,container就是我们传进来的ExplosionField,它赋给了mContainer。我们注意到,ExplosionField重写了start方法:

  1. @Override  
  2. public void start() {  
  3.     super.start();  
  4.     mContainer.invalidate(mBound);  
  5. }  
在start方法中调用了ExplosionField的draw方法,那我们看看ExplosionField方法中的draw方法:

  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     for (ExplosionAnimator explosion : mExplosions) {  
  5.         explosion.draw(canvas);  
  6.     }  
  7. }  
好啊,这里竟然又调用了ExplosionAnimator的draw方法,那我们再看看ExplosionAnimator中的draw方法:

  1. public boolean draw(Canvas canvas) {  
  2.     if (!isStarted()) {  
  3.         return false;  
  4.     }  
  5.     for (Particle particle : mParticles) {  
  6.         particle.advance((float) getAnimatedValue());  
  7.         if (particle.alpha > 0f) {  
  8.             mPaint.setColor(particle.color);  
  9.             mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha));  
  10.             canvas.drawCircle(particle.cx, particle.cy, particle.radius,  
  11.                     mPaint);  
  12.         }  
  13.     }  
  14.     mContainer.invalidate();  
  15.     return true;  
  16. }  

在ExplosionAnimator的draw方法中,我们看到,在for循环中会将我们的15×15个Parcicle都画出来,然后又回去调用ExplosionField中的draw方法,如此循环往复互相之间不断调用,直到Particle的alpha属性为0时停止调用。

看到这里,我们对ExplosionField有了一个基本的了解,这个时候我们再回过头看ExplosionField中的explode方法,我们注意到有一个List集合专门用来存放ExplosionAnimator,这是由于我们的一个页面上可能同时有多个View被粉碎,每一个被粉碎的View对应一个单独的ExplosionAnimator,当该动画执行完了之后,就把它从List集合中移除。


关于粉碎View的事就说这么多。


Demo下载https://github.com/lenve/ExplosionFieldTest

目录(?)[+]

小米平板卸载软件的时候,会有一个粉碎的效果,看起来很拉风,GitHub上有一个开源控件可以实现这个效果,我们一起来看看。先来看看效果图:


看起来不错吧,那我们今天就来详细说说ExplosionField的使用和它内部的实现原理吧。

1.获得一个ExplosionField

ExplosionField是GitHub上的一个开源项目,我们直接在GitHub上下载就可以了。

下载地址https://github.com/tyrantgit/ExplosionField

下载好之后我们直接将tyrantgit文件夹拷贝到我们的项目中就可以使用了,该文件夹的位置在:ExplosionField-master\ExplosionField-master\explosionfield\src\main\java中。

注意:使用ExplosionField要求我们的jre版本在1.7以上。


2.ExplosionField的使用

布局文件很简单,就是四个ImageView:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <LinearLayout  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_weight="1"  
  11.         android:gravity="center"  
  12.         android:orientation="horizontal" >  
  13.   
  14.         <ImageView  
  15.             android:id="@+id/iv1"  
  16.             android:layout_width="72dp"  
  17.             android:layout_height="72dp"  
  18.             android:layout_weight="1"  
  19.             android:onClick="onClick"  
  20.             android:src="@drawable/p1" />  
  21.   
  22.         <ImageView  
  23.             android:id="@+id/iv2"  
  24.             android:layout_width="72dp"  
  25.             android:layout_height="72dp"  
  26.             android:layout_weight="1"  
  27.             android:onClick="onClick"  
  28.             android:src="@drawable/p2" />  
  29.     </LinearLayout>  
  30.   
  31.   
  32.     <LinearLayout  
  33.         android:layout_width="match_parent"  
  34.         android:layout_height="wrap_content"  
  35.         android:layout_weight="1"  
  36.         android:orientation="horizontal" >  
  37.   
  38.         <ImageView  
  39.             android:id="@+id/iv3"  
  40.             android:layout_width="72dp"  
  41.             android:layout_height="72dp"  
  42.             android:layout_weight="1"  
  43.             android:onClick="onClick"  
  44.             android:src="@drawable/p3" />  
  45.   
  46.         <ImageView  
  47.             android:id="@+id/iv4"  
  48.             android:layout_width="72dp"  
  49.             android:layout_height="72dp"  
  50.             android:layout_weight="1"  
  51.             android:onClick="onClick"  
  52.             android:src="@drawable/p4" />  
  53.     </LinearLayout>  
  54.   
  55. </LinearLayout>  

在MainActivity中,我们拿到一个ExplosionField的实例,然后通过执行它的explode方法就可以实现View的粉碎效果了。

  1. public class MainActivity extends Activity {  
  2.   
  3.     private ExplosionField explosionField;  
  4.     private ImageView iv1, iv2, iv3, iv4;  
  5.   
  6.     @Override  
  7.     protected void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.         setContentView(R.layout.activity_main);  
  10.         iv1 = (ImageView) this.findViewById(R.id.iv1);  
  11.         iv2 = (ImageView) this.findViewById(R.id.iv2);  
  12.         iv3 = (ImageView) this.findViewById(R.id.iv3);  
  13.         iv4 = (ImageView) this.findViewById(R.id.iv4);  
  14.         explosionField = ExplosionField.attach2Window(this);  
  15.     }  
  16.   
  17.     public void onClick(View v) {  
  18.         switch (v.getId()) {  
  19.         case R.id.iv1:  
  20.             explosionField.explode(iv1);  
  21.             iv1.setVisibility(View.INVISIBLE);  
  22.             break;  
  23.         case R.id.iv2:  
  24.             explosionField.explode(iv2);  
  25.             iv2.setVisibility(View.INVISIBLE);  
  26.             break;  
  27.         case R.id.iv3:  
  28.             explosionField.explode(iv3);  
  29.             iv3.setVisibility(View.INVISIBLE);  
  30.             break;  
  31.         case R.id.iv4:  
  32.             explosionField.explode(iv4);  
  33.             iv4.setVisibility(View.INVISIBLE);  
  34.             break;  
  35.         }  
  36.     }  
  37. }  

使用起来是如此的方便,这里我要特别说一句,为什么粉碎完成之后还要将控件隐藏,这是因为View粉碎之后,我们就看不到了,但是实际上它还在页面上,如果这个View本身有点击事件,那么这个时候你点击它粉碎前的位置,点击事件还是能够响应,将控件隐藏之后就可以避免这个问题了。说了这么多,我们来看看这个效果究竟是怎么实现的。


3.源码解读

通过前面两步,我们已经知道,跟这个效果有关的就是三个类,那么我们这里就从explosionField = ExplosionField.attach2Window(this);这行代码所执行的attach2Window(this);方法看起:

  1. public static ExplosionField attach2Window(Activity activity) {  
  2.     ViewGroup rootView = (ViewGroup) activity  
  3.             .findViewById(Window.ID_ANDROID_CONTENT);  
  4.     ExplosionField explosionField = new ExplosionField(activity);  
  5.     rootView.addView(explosionField, new ViewGroup.LayoutParams(  
  6.             ViewGroup.LayoutParams.MATCH_PARENT,  
  7.             ViewGroup.LayoutParams.MATCH_PARENT));  
  8.     return explosionField;  
  9. }  

首先我们要知道,ExplosionField继承自View,它本身就是一个View,那么在attach2Window方法中,我们先拿到一个rootView,这个rootView其实就是我们setContentView(R.layout.activity_main);所设置的View,然后我们new一个ExplosionField并把它添加到rootView当中,并把它的宽高都设置为MATCH_PARENT,这样就可以保证View爆炸产生的碎片可以被绘制在整个Activity区域。

好了,拿到一个ExplosionField对象之后,我们就可以粉碎一个View了,粉碎View执行的方法是explode,这个方法需要我们把要粉碎的View作为一个参数传入进去,我们看看这个方法:

  1. public void explode(final View view) {  
  2.     Rect r = new Rect();  
  3.     view.getGlobalVisibleRect(r);  
  4.     int[] location = new int[2];  
  5.     getLocationOnScreen(location);  
  6.     r.offset(-location[0], -location[1]);  
  7.     r.inset(-mExpandInset[0], -mExpandInset[1]);  
  8.     int startDelay = 100;  
  9.     ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150);  
  10.     animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
  11.   
  12.         Random random = new Random();  
  13.   
  14.         @Override  
  15.         public void onAnimationUpdate(ValueAnimator animation) {  
  16.             view.setTranslationX((random.nextFloat() - 0.5f)  
  17.                     * view.getWidth() * 0.05f);  
  18.             view.setTranslationY((random.nextFloat() - 0.5f)  
  19.                     * view.getHeight() * 0.05f);  
  20.   
  21.         }  
  22.     });  
  23.     animator.start();  
  24.     view.animate().setDuration(150).setStartDelay(startDelay).scaleX(0f)  
  25.             .scaleY(0f).alpha(0f).start();  
  26.     explode(Utils.createBitmapFromView(view), r, startDelay,  
  27.             ExplosionAnimator.DEFAULT_DURATION);  
  28. }  
这里的代码我们可以分为4部分来理解:

第一部分:2-7行

这一段代码我们主要是用来获取要粉碎View的一个可视区域,拿到这个Rect之后,我们再拿到ExplosionField在当前页面中的坐标,然后根据这个坐标对Rect进行平移,平移之后就可以让粉碎的效果在ExplosionField中显示,最后我们还要将Rect的区域扩大一下,默认情况下,横向扩大64dp,纵向扩大64dp,mExpandInset的初始化在ExplosionField的构造方法中调用,最终拿到的Rect我们会在第四部分使用。

第二部分:8-23行

这一段代码主要是是完成粉碎前的振动效果,一个View要被粉碎了,吓得它先打个哆嗦。这里的实现还是比较简单的,先是生成一个随机数,根据这个随机数算出View在X轴和Y轴平移的距离,反复的平移就形成了振动效果。

第三部分:24-25行

这一段代码是将View隐藏,隐藏的方式就是让scaleX和scaleY、alpha最终都变为0f。

第四部分:26-27行

这一段代码是粉碎View的核心代码,我们下面分析。
好了,这一段代码算是一个准备工作,下面我们看看上面第四部分提到的这个explode的重载方法,这里一共需要四个参数,第一个参数是一个Bitmap,这个Bitmap根据我们传入的View创建,第二个参数就是我们上面第一部分提到的那个Rect,第三个参数是一个启动延迟时间,第四个参数是动画默认的执行时间,四个参数中,后面两个都是固定值,第二个参数我们上文已经说过,这里我们主要看看第一个参数的获得:

  1. public static Bitmap createBitmapFromView(View view) {  
  2.     if (view instanceof ImageView) {  
  3.         Drawable drawable = ((ImageView) view).getDrawable();  
  4.         if (drawable != null && drawable instanceof BitmapDrawable) {  
  5.             return ((BitmapDrawable) drawable).getBitmap();  
  6.         }  
  7.     }  
  8.     view.clearFocus();  
  9.     Bitmap bitmap = createBitmapSafely(view.getWidth(),  
  10.             view.getHeight(), Bitmap.Config.ARGB_8888, 1);  
  11.     if (bitmap != null) {  
  12.         synchronized (sCanvas) {  
  13.             Canvas canvas = sCanvas;  
  14.             canvas.setBitmap(bitmap);  
  15.             view.draw(canvas);  
  16.             canvas.setBitmap(null);  
  17.         }  
  18.     }  
  19.     return bitmap;  
  20. }  
  21.   
  22. public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) {  
  23.     try {  
  24.         return Bitmap.createBitmap(width, height, config);  
  25.     } catch (OutOfMemoryError e) {  
  26.         e.printStackTrace();  
  27.         if (retryCount > 0) {  
  28.             System.gc();  
  29.             return createBitmapSafely(width, height, config, retryCount - 1);  
  30.         }  
  31.         return null;  
  32.     }  
  33. }  

根据我们传入的View,我们得到一个Bitmap,如果这个View本身就是一个ImageView,那么我们会比较容易获得一个Bitmap,如果这个View不是ImageView,那么我们根据这个View的大小创建一个空的Bitmap,在创建的过程中,如果发生了OOM,我们会尝试执行一次System.gc,然后重新创建,如果还创建失败,那么就会返回一个null。拿到Bitmap之后,我们把这个Bitmap设置为一个Canvas的底布,然后把View绘制在Canvas上,最后再把Canvas的底布设置为null,同时将Bitmap返回。

经过上面的步骤,我们就可以拿到一个根据我们要粉碎的View创建的bitmap,然后我们就来看看explode的重载方法:

  1. public void explode(Bitmap bitmap, Rect bound, long startDelay,  
  2.         long duration) {  
  3.     final ExplosionAnimator explosion = new ExplosionAnimator(this, bitmap,  
  4.             bound);  
  5.     explosion.addListener(new AnimatorListenerAdapter() {  
  6.         @Override  
  7.         public void onAnimationEnd(Animator animation) {  
  8.             mExplosions.remove(animation);  
  9.         }  
  10.     });  
  11.     explosion.setStartDelay(startDelay);  
  12.     explosion.setDuration(duration);  
  13.     mExplosions.add(explosion);  
  14.     explosion.start();  
  15. }  
到了这里,我们先来看看ExplosionAnimator这个类,这个类继承自ValueAnimator,我们来看看ExplosionField的构造方法:
  1. public ExplosionAnimator(View container, Bitmap bitmap, Rect bound) {  
  2.     mPaint = new Paint();  
  3.     mBound = new Rect(bound);  
  4.     int partLen = 15;  
  5.     mParticles = new Particle[partLen * partLen];  
  6.     Random random = new Random(System.currentTimeMillis());  
  7.     int w = bitmap.getWidth() / (partLen + 2);  
  8.     int h = bitmap.getHeight() / (partLen + 2);  
  9.     for (int i = 0; i < partLen; i++) {  
  10.         for (int j = 0; j < partLen; j++) {  
  11.             mParticles[(i * partLen) + j] = generateParticle(  
  12.                     bitmap.getPixel((j + 1) * w, (i + 1) * h), random);  
  13.         }  
  14.     }  
  15.     mContainer = container;  
  16.     setFloatValues(0f, END_VALUE);  
  17.     setInterpolator(DEFAULT_INTERPOLATOR);  
  18.     setDuration(DEFAULT_DURATION);  
  19. }  
在这里我们看到了View被粉碎的方式,一个View一共被分为15×15个Particle,通过generateParticle方法可以获得这一个一个的Particle,每一个Particle的颜色就是bitmap上对应的颜色,这些Particle存在一个mParticles数组中,container就是我们传进来的ExplosionField,它赋给了mContainer。我们注意到,ExplosionField重写了start方法:

  1. @Override  
  2. public void start() {  
  3.     super.start();  
  4.     mContainer.invalidate(mBound);  
  5. }  
在start方法中调用了ExplosionField的draw方法,那我们看看ExplosionField方法中的draw方法:

  1. @Override  
  2. protected void onDraw(Canvas canvas) {  
  3.     super.onDraw(canvas);  
  4.     for (ExplosionAnimator explosion : mExplosions) {  
  5.         explosion.draw(canvas);  
  6.     }  
  7. }  
好啊,这里竟然又调用了ExplosionAnimator的draw方法,那我们再看看ExplosionAnimator中的draw方法:

  1. public boolean draw(Canvas canvas) {  
  2.     if (!isStarted()) {  
  3.         return false;  
  4.     }  
  5.     for (Particle particle : mParticles) {  
  6.         particle.advance((float) getAnimatedValue());  
  7.         if (particle.alpha > 0f) {  
  8.             mPaint.setColor(particle.color);  
  9.             mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha));  
  10.             canvas.drawCircle(particle.cx, particle.cy, particle.radius,  
  11.                     mPaint);  
  12.         }  
  13.     }  
  14.     mContainer.invalidate();  
  15.     return true;  
  16. }  

在ExplosionAnimator的draw方法中,我们看到,在for循环中会将我们的15×15个Parcicle都画出来,然后又回去调用ExplosionField中的draw方法,如此循环往复互相之间不断调用,直到Particle的alpha属性为0时停止调用。

看到这里,我们对ExplosionField有了一个基本的了解,这个时候我们再回过头看ExplosionField中的explode方法,我们注意到有一个List集合专门用来存放ExplosionAnimator,这是由于我们的一个页面上可能同时有多个View被粉碎,每一个被粉碎的View对应一个单独的ExplosionAnimator,当该动画执行完了之后,就把它从List集合中移除。


关于粉碎View的事就说这么多。


Demo下载https://github.com/lenve/ExplosionFieldTest
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:2886次
    • 积分:62
    • 等级:
    • 排名:千里之外
    • 原创:1篇
    • 转载:13篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档