5分钟快速实现Android爆炸破碎酷炫动画特效的示例

5分钟快速实现Android爆炸破碎酷炫动画特效的示例,第1张

概述这个破碎动画,是一种类似小米系统删除应用时的爆炸破碎效果的动画。效果图展示

这个破碎动画,是一种类似小米系统删除应用时的爆炸破碎效果的动画。

效果图展示

先来看下是怎样的动效,要是感觉不是理想的学习目标,就跳过,避免浪费大家的时间。��

@H_502_6@

源码在这里:point_right: https://github.com/ReadyShowShow/explosion

一行代码即可调用该动画

new ExplosionFIEld(this).explode(vIEw,null))

下面开始我们酷炫的AndroID动画特效正式讲解:point_down:

先来个整体结构的把握

整体结构非常简单明了,新老从业者都可快速看懂,容易把握学习。

./|-- explosion| |-- MainActivity.java (测试爆炸破碎动效的主界面)| |-- animation(爆炸破碎动效有关的类均在这里)| | |-- ExplosionAnimator.java(爆炸动画)| | |-- ExplosionFIEld.java(爆炸破碎动画所依赖的VIEw)| | `-- ParticleModel.java(每个破碎后的粒子的model,颜色、位置、大小等)| `-- utils|  `-- UIUtils.java(计算状态栏高度的工具类)`-- tree.txt

庖丁解牛

下面开始每个类的详细分析

本着从简到繁、由表及里的原则,详细讲解每个类

MainActivity.java

MainActivity.java是测试动效的界面,该Activity内部有7个测试按钮。该类做的事情非常单纯,就是给每个VIEw分别绑定click点击事件,让VIEw在点击时能触发爆炸破碎动画。

/** * 说明:测试的界面 * 作者:Jian * 时间:2017/12/26. */public class MainActivity extends AppCompatActivity { /**  * 加载布局文件,添加点击事件  */ @OverrIDe protected voID onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentVIEw(R.layout.activity_main);  initVIEwsClick(); } /**  * 添加点击事件的实现  */ private voID initVIEwsClick() {  // 为单个VIEw添加点击事件  final VIEw Title = findVIEwByID(R.ID.Title_tv);  Title.setonClickListener(v ->    new ExplosionFIEld(MainActivity.this).explode(Title,null));  // 为中间3个VIEw添加点击事件  setSelfAndChilddisappearOnClick(findVIEwByID(R.ID.Title_disappear_ll));  // 为下面3个VIEw添加点击事件  setSelfAndChilddisappearandAppearOnClick(findVIEwByID(R.ID.Title_disappear_and_appear_ll));  // 跳转到github网页的点击事件  findVIEwByID(R.ID.github_tv).setonClickListener((vIEw) -> {   Intent intent = new Intent();   intent.setAction(Intent.ACTION_VIEW);   Uri content_url = Uri.parse(getString(R.string.github));   intent.setData(content_url);   startActivity(intent);  }); } /**  * 为自己以及子VIEw添加破碎动画,动画结束后,把VIEw消失掉  * @param vIEw 可能是VIEwGroup的vIEw  */ private voID setSelfAndChilddisappearOnClick(final VIEw vIEw) {  if (vIEw instanceof VIEwGroup) {   VIEwGroup vIEwGroup = (VIEwGroup) vIEw;   for (int i = 0; i < vIEwGroup.getChildCount(); i++) {    setSelfAndChilddisappearOnClick(vIEwGroup.getChildAt(i));   }  } else {   vIEw.setonClickListener(v ->     new ExplosionFIEld(MainActivity.this).explode(vIEw,new AnimatorListenerAdapter() {        @OverrIDe        public voID onAnimationEnd(Animator animation) {         super.onAnimationEnd(animation);         vIEw.setVisibility(VIEw.GONE);        }       }));  } } /**  * 为自己以及子VIEw添加破碎动画,动画结束后,VIEw自动出现  * @param vIEw 可能是VIEwGroup的vIEw  */ private voID setSelfAndChilddisappearandAppearOnClick(final VIEw vIEw) {  if (vIEw instanceof VIEwGroup) {   VIEwGroup vIEwGroup = (VIEwGroup) vIEw;   for (int i = 0; i < vIEwGroup.getChildCount(); i++) {    setSelfAndChilddisappearandAppearOnClick(vIEwGroup.getChildAt(i));   }  } else {   vIEw.setonClickListener(v ->     new ExplosionFIEld(MainActivity.this).explode(vIEw,null));  } }}

ParticleModel.java

ParticleModel.java是包含一个粒子的所有信息的model。advance方法根据值动画返回的进度计算出粒子的位置和颜色等信息

/** * 说明:爆破粒子,每个移动与渐变的小块 * 作者:Jian * 时间:2017/12/26. */class ParticleModel { // 默认小球宽高 static final int PART_WH = 8; // 随机数,随机出位置和大小 static Random random = new Random(); //center x of circle float cx; //center y of circle float cy; // 半径 float radius; // 颜色 int color; // 透明度 float Alpha; // 整体边界 Rect mBound; ParticleModel(int color,Rect bound,Point point) {  int row = point.y; //行是高  int column = point.x; //列是宽  this.mBound = bound;  this.color = color;  this.Alpha = 1f;  this.radius = PART_WH;  this.cx = bound.left + PART_WH * column;  this.cy = bound.top + PART_WH * row; } // 每一步动画都得重新计算出自己的状态值 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()); }}

ExplosionAnimation.java

ExlosionAnimation.java是动画类,是一个值动画,在值动画每次产生一个值的时候,就计算出整个爆炸破碎动效内的全部粒子的状态。这些状态交由使用的VIEw在渲染时进行显示。

/** * 说明:爆炸动画类,让离子移动和控制离子透明度 * 作者:Jian * 时间:2017/12/26. */class ExplosionAnimator extends ValueAnimator { private static final int DEFAulT_DURATION = 1500; private ParticleModel[][] mParticles; private Paint mPaint; private VIEw mContainer; public ExplosionAnimator(VIEw vIEw,Bitmap bitmap,Rect bound) {  setfloatValues(0.0f,1.0f);  setDuration(DEFAulT_DURATION);  mPaint = new Paint();  mContainer = vIEw;  mParticles = generateParticles(bitmap,bound); } // 生成粒子,按行按列生成全部粒子 private ParticleModel[][] generateParticles(Bitmap bitmap,Rect bound) {  int w = bound.wIDth();  int h = bound.height();  // 横向粒子的个数  int horizontalCount = w / ParticleModel.PART_WH;  // 竖向粒子的个数  int verticalCount = h / ParticleModel.PART_WH;  // 粒子宽度  int bitmapPartWIDth = bitmap.getWIDth() / horizontalCount;  // 粒子高度  int bitmapPartHeight = bitmap.getHeight() / verticalCount;  ParticleModel[][] particles = new ParticleModel[verticalCount][horizontalCount];  for (int row = 0; row < verticalCount; row++) {   for (int column = 0; column < horizontalCount; column++) {    //取得当前粒子所在位置的颜色    int color = bitmap.getPixel(column * bitmapPartWIDth,row * bitmapPartHeight);    Point point = new Point(column,row);    particles[row][column] = new ParticleModel(color,bound,point);   }  }  return particles; } // 由vIEw调用,在VIEw上绘制全部的粒子 voID draw(Canvas canvas) {  // 动画结束时停止  if (!isstarted()) {   return;  }  // 遍历粒子,并绘制在VIEw上  for (ParticleModel[] particle : mParticles) {   for (ParticleModel p : particle) {    p.advance((float) getAnimatedValue());    mPaint.setcolor(p.color);    // 错误的设置方式只是这样设置,透明色会显示为黑色    // mPaint.setAlpha((int) (255 * p.Alpha));     // 正确的设置方式,这样透明颜色就不是黑色了    mPaint.setAlpha((int) (color.Alpha(p.color) * p.Alpha));    canvas.drawCircle(p.cx,p.cy,p.radius,mPaint);   }  }  mContainer.invalIDate(); } @OverrIDe public voID start() {  super.start();  mContainer.invalIDate(); }}

ExplosionFIEld.java

ExplosionFIEld.java是真实执行上面ExplosionAnimator。ExplosionFIEld会创建一个VIEw并依附在Activity的根VIEw上。

/** * 说明:每次爆炸时,创建一个覆盖全屏的VIEw,这样的话,不管要爆炸的VIEw在任何位置都能显示爆炸效果 * 作者:Jian * 时间:2017/12/26. */public class ExplosionFIEld extends VIEw { private static final String TAG = "ExplosionFIEld"; private static final Canvas mCanvas = new Canvas(); private ExplosionAnimator animator; public ExplosionFIEld(Context context) {  super(context); } @OverrIDe protected voID onDraw(Canvas canvas) {  super.onDraw(canvas);  animator.draw(canvas); } /**  * 执行爆破破碎动画  */ public voID explode(final VIEw vIEw,final AnimatorListenerAdapter Listener) {  Rect rect = new Rect();  vIEw.getGlobalVisibleRect(rect); //得到vIEw相对于整个屏幕的坐标  rect.offset(0,-UIUtils.statusbarHeignth()); //去掉状态栏高度  animator = new ExplosionAnimator(this,createBitmapFromVIEw(vIEw),rect);  // 接口回调  animator.addListener(new Animator.AnimatorListener() {   @OverrIDe   public voID onAnimationStart(Animator animation) {    if (Listener != null) Listener.onAnimationStart(animation);    // 延时添加到界面上    attach2Activity((Activity) getContext());    // 让被爆炸的VIEw消失(爆炸的VIEw是新创建的VIEw,原VIEw本身不会发生任何变化)    vIEw.animate().Alpha(0f).setDuration(150).start();   }   @OverrIDe   public voID onAnimationEnd(Animator animation) {    if (Listener != null) Listener.onAnimationEnd(animation);    // 从界面中移除    removeFromActivity((Activity) getContext());    // 让被爆炸的VIEw显示(爆炸的VIEw是新创建的VIEw,原VIEw本身不会发生任何变化)    vIEw.animate().Alpha(1f).setDuration(150).start();   }   @OverrIDe   public voID onAnimationCancel(Animator animation) {    if (Listener != null) Listener.onAnimationCancel(animation);   }   @OverrIDe   public voID onAnimationRepeat(Animator animation) {    if (Listener != null) Listener.onAnimationRepeat(animation);   }  });  animator.start(); } private Bitmap createBitmapFromVIEw(VIEw vIEw) {//   为什么屏蔽以下代码段?//   如果ImageVIEw直接得到位图,那么当它设置背景(backgroud)时,不会读取到背景颜色//  if (vIEw instanceof ImageVIEw) {//   Drawable drawable = ((ImageVIEw)vIEw).getDrawable();//   if (drawable != null && drawable instanceof BitmapDrawable) {//    return ((BitmapDrawable) drawable).getBitmap();//   }//  }  //vIEw.clearFocus(); //不同焦点状态显示的可能不同――(azz:不同就不同有什么关系?)  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; } /**  * 将创建的ExplosionFIEld添加到Activity上  */ private voID attach2Activity(Activity activity) {  VIEwGroup rootVIEw = activity.findVIEwByID(Window.ID_ANDROID_CONTENT);  VIEwGroup.LayoutParams lp = new VIEwGroup.LayoutParams(    VIEwGroup.LayoutParams.MATCH_PARENT,VIEwGroup.LayoutParams.MATCH_PARENT);  rootVIEw.addVIEw(this,lp); } /**  * 将ExplosionFIEld从Activity上移除  */ private voID removeFromActivity(Activity activity) {  VIEwGroup rootVIEw = activity.findVIEwByID(Window.ID_ANDROID_CONTENT);  rootVIEw.removeVIEw(this); }}

动画执行时为什么要创建一个新VIEw(ExplosionFIEld)

其实上面的动画类ExplosionAnimator已经实现了核心功能,直接在原VIEw上使用该动画应该是没问题的。为什么还要引入一个ExplosionFIEld类呢?动画的执行为什么不能直接在原本的VIEw上执行呢?偏偏要在一个看似多余的ExplosionFIEld对象上执行呢。

这里就得从AndroID下VIEw绘制原理来解释了:AndroID下的VIEw都有一个Bound,在VIEw进行measure和layout的时候,已经确定了VIEw的大小和位置,如果要在这个VIEw上进行动画的话,就会出现动画只能在vIEw大小范围内进行展现。当然了,也不是说在原来VIEw上一定不能实现这一动效,就是相当复杂,要在动画执行过程中,不断改变原VIEw的大小和VIEw的属性等信息,相当复杂。

在性能还行的前提下,要优先代码的整洁度,尽量避免为了优化的性能,而舍弃整洁清爽的代码。一般来说,过度的优化,并没有给用户带来太多体验上的提升,反而给项目带来了巨大的维护难度。

UIUtils.java

UIUtils是关于UI的工具类,没啥可说的

public class UIUtils { public static int dp2px(double dpi) {  return (int) (Resources.getSystem().getdisplayMetrics().density * dpi + 0.5f); } public static int statusbarHeignth() {  return dp2px(25); }}

结束

源码:point_right: https://github.com/ReadyShowShow/explosion

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

总结

以上是内存溢出为你收集整理的5分钟快速实现Android爆炸破碎酷炫动画特效的示例全部内容,希望文章能够帮你解决5分钟快速实现Android爆炸破碎酷炫动画特效的示例所遇到的程序开发问题。

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

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

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

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

发表评论

登录后才能评论

评论列表(0条)

保存