关闭

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

标签: androidExplosionFieldView粉碎
3097人阅读 评论(4) 收藏 举报
分类:

小米平板卸载软件的时候,会有一个粉碎的效果,看起来很拉风,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:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="horizontal" >

        <ImageView
            android:id="@+id/iv1"
            android:layout_width="72dp"
            android:layout_height="72dp"
            android:layout_weight="1"
            android:onClick="onClick"
            android:src="@drawable/p1" />

        <ImageView
            android:id="@+id/iv2"
            android:layout_width="72dp"
            android:layout_height="72dp"
            android:layout_weight="1"
            android:onClick="onClick"
            android:src="@drawable/p2" />
    </LinearLayout>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="horizontal" >

        <ImageView
            android:id="@+id/iv3"
            android:layout_width="72dp"
            android:layout_height="72dp"
            android:layout_weight="1"
            android:onClick="onClick"
            android:src="@drawable/p3" />

        <ImageView
            android:id="@+id/iv4"
            android:layout_width="72dp"
            android:layout_height="72dp"
            android:layout_weight="1"
            android:onClick="onClick"
            android:src="@drawable/p4" />
    </LinearLayout>

</LinearLayout>

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

public class MainActivity extends Activity {

	private ExplosionField explosionField;
	private ImageView iv1, iv2, iv3, iv4;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		iv1 = (ImageView) this.findViewById(R.id.iv1);
		iv2 = (ImageView) this.findViewById(R.id.iv2);
		iv3 = (ImageView) this.findViewById(R.id.iv3);
		iv4 = (ImageView) this.findViewById(R.id.iv4);
		explosionField = ExplosionField.attach2Window(this);
	}

	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.iv1:
			explosionField.explode(iv1);
			iv1.setVisibility(View.INVISIBLE);
			break;
		case R.id.iv2:
			explosionField.explode(iv2);
			iv2.setVisibility(View.INVISIBLE);
			break;
		case R.id.iv3:
			explosionField.explode(iv3);
			iv3.setVisibility(View.INVISIBLE);
			break;
		case R.id.iv4:
			explosionField.explode(iv4);
			iv4.setVisibility(View.INVISIBLE);
			break;
		}
	}
}

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


3.源码解读

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

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

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

	public void explode(final View view) {
		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]);
		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();
		explode(Utils.createBitmapFromView(view), r, startDelay,
				ExplosionAnimator.DEFAULT_DURATION);
	}
这里的代码我们可以分为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,第三个参数是一个启动延迟时间,第四个参数是动画默认的执行时间,四个参数中,后面两个都是固定值,第二个参数我们上文已经说过,这里我们主要看看第一个参数的获得:

    public static Bitmap createBitmapFromView(View view) {
        if (view instanceof ImageView) {
            Drawable drawable = ((ImageView) view).getDrawable();
            if (drawable != null && drawable instanceof BitmapDrawable) {
                return ((BitmapDrawable) drawable).getBitmap();
            }
        }
        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);
            }
        }
        return bitmap;
    }

    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, height, config, retryCount - 1);
            }
            return null;
        }
    }

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

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

	public void explode(Bitmap bitmap, Rect bound, long startDelay,
			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();
	}
到了这里,我们先来看看ExplosionAnimator这个类,这个类继承自ValueAnimator,我们来看看ExplosionField的构造方法:
	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);
	}
在这里我们看到了View被粉碎的方式,一个View一共被分为15×15个Particle,通过generateParticle方法可以获得这一个一个的Particle,每一个Particle的颜色就是bitmap上对应的颜色,这些Particle存在一个mParticles数组中,container就是我们传进来的ExplosionField,它赋给了mContainer。我们注意到,ExplosionField重写了start方法:

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

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		for (ExplosionAnimator explosion : mExplosions) {
			explosion.draw(canvas);
		}
	}
好啊,这里竟然又调用了ExplosionAnimator的draw方法,那我们再看看ExplosionAnimator中的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;
	}

在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


5
0
查看评论

开源项目ExplosionField(爆炸特效)源码分析

开源项目ExplosionField(爆炸特效)源码分析 http://blog.csdn.net/u013022222/article/details/48995105 这是前几日在朋友圈传疯了的开源项目 如果没记错的话 小米手机卸载应用的时候就是使用的这个效果 于是我去git...
  • zdy0_2004
  • zdy0_2004
  • 2015-10-11 01:50
  • 3066

第三方开源控件ExplosionField源码解析

笑谈风云,一语定乾坤。今天和大伙分享一个有意思的开源控件。 简介 先上一张图,大家感受下。 是不是酷炫?是不是似曾相识?在小米系统MIUI中的删除APK功能也是有上面这种粒子炸裂的效果的。下面给出github地址 https://github.com/tyrantgit/ExplosionFie...
  • shirakawakanaki
  • shirakawakanaki
  • 2016-12-07 14:06
  • 933

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

小米平板卸载软件的时候,会有一个粉碎的效果,看起来很拉风,GitHub上有一个开源控件可以实现这个效果,我们一起来看看。先来看看效果图:看起来不错吧,那我们今天就来详细说说ExplosionField的使用和它内部的实现原理吧。1.获得一个ExplosionFieldExplosionField是G...
  • u012702547
  • u012702547
  • 2015-11-19 21:50
  • 3097

Explosion Field

  • 2015-10-09 09:53
  • 5.86MB
  • 下载

烟花爆炸色彩效果—ExplosionField

GitHub上的源码1:介绍 ExplosionField简单翻译过来是爆炸场的意思,应用也是很炫的一种第三方,现在应用的小米手机上,软件应用卸载。2:依赖 dependencies { compile 'tyrantgit:explosionfield:1.0.1' }3...
  • qq_37293612
  • qq_37293612
  • 2017-01-14 15:06
  • 484

Windows文件系统以及文件粉碎原理

1.1硬盘揭秘: 物理存储方式: 目前的存储方式有磁存储,电存储,光存储。U盘就是电存储,VCD,DVD光盘用的是光存储,我们计算机用的硬盘就是用的磁存储。各种存储方式在物理存储介质不同外,在逻辑层面上都是基于文件系统结构的方式,也就是用户看到的任何文件数据都是已文件和目录的形式展现在我们面前。...
  • soft2967
  • soft2967
  • 2012-04-05 16:39
  • 8152

开源项目ExplosionField(爆炸特效)源码分析

这是前几日在朋友圈传疯了的开源项目 如果没记错的话 小米手机卸载应用的时候就是使用的这个效果 于是我去github fork 了这个项目 地址如下: 点击打开链接 效果图: 我使用的IDE 是 android studio 我把源码 和范例程序简单的移植到了android stud...
  • u013022222
  • u013022222
  • 2015-10-09 09:55
  • 2276

【Android效果集】学习ExplosionField之粒子破碎效果

前段时间在某效果网站看到开源项目【ExplosionField】非常喜欢,于是自己跟着源码学习着去做了做。跟源码效果有一点区别,我都是尽力读懂源码然后用自己的理解写出来,源码有些看不懂的地方,我也就没有用到,因为自己的代码要保证自己都能看懂。最后效果如下: (本文适合有一年Android开发经验者...
  • XieYupeng520
  • XieYupeng520
  • 2015-12-03 17:47
  • 9234

C# 系统应用之调用SDelete程序粉碎文件及基础原理知识

这篇文章主要讲述的是Windows文件系统及调用SDelete程序彻底粉碎文件.涉及到文件系统和文件的知识,windows下的FAT和NTFS技术基础性讲解.一.Windows文件系统 二.恢复文件简单原理 三.cmd调用SDelete程序及参数 四.C#实现粉碎文件(未实现) 希望文章对大家有所帮...
  • Eastmount
  • Eastmount
  • 2014-04-19 15:45
  • 2871

文件粉碎也删不掉 教你暴力删除无法删除的文件

新建记事本,在记事本内容中写入以下命令 DEL /F /A /Q \\?\%1 RD /S /Q \\?\%1   将文件另存为,一定要是另存为。   保存类型选择“所有文件”,名称随你取,只要后缀为“.bat”保存。   然后将删除不掉的文...
  • yongzhen150
  • yongzhen150
  • 2016-03-16 15:29
  • 7125
    更多JavaEE资料请关注公众号
    个人资料
    • 访问:1929327次
    • 积分:18932
    • 等级:
    • 排名:第566名
    • 原创:326篇
    • 转载:1篇
    • 译文:2篇
    • 评论:1009条
    博客专栏
    最新评论