5分鐘快速實(shí)現(xiàn)Android爆炸破碎酷炫動(dòng)畫(huà)特效的示例

這個(gè)破碎動(dòng)畫(huà),是一種類似小米系統(tǒng)刪除應(yīng)用時(shí)的爆炸破碎效果的動(dòng)畫(huà)。

雙陽(yáng)網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)公司,雙陽(yáng)網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為雙陽(yáng)成百上千提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)公司要多少錢,請(qǐng)找那個(gè)售后服務(wù)好的雙陽(yáng)做網(wǎng)站的公司定做!

效果圖展示

先來(lái)看下是怎樣的動(dòng)效,要是感覺(jué)不是理想的學(xué)習(xí)目標(biāo),就跳過(guò),避免浪費(fèi)大家的時(shí)間。��

5分鐘快速實(shí)現(xiàn)Android爆炸破碎酷炫動(dòng)畫(huà)特效的示例

源碼在這里:point_right: https://github.com/ReadyShowShow/explosion

一行代碼即可調(diào)用該動(dòng)畫(huà)

new ExplosionField(this).explode(view, null))

下面開(kāi)始我們酷炫的Android動(dòng)畫(huà)特效正式講解:point_down:

先來(lái)個(gè)整體結(jié)構(gòu)的把握

整體結(jié)構(gòu)非常簡(jiǎn)單明了,新老從業(yè)者都可快速看懂,容易把握學(xué)習(xí)。

./
|-- explosion
| |-- MainActivity.java (測(cè)試爆炸破碎動(dòng)效的主界面)
| |-- animation(爆炸破碎動(dòng)效有關(guān)的類均在這里)
| | |-- ExplosionAnimator.java(爆炸動(dòng)畫(huà))
| | |-- ExplosionField.java(爆炸破碎動(dòng)畫(huà)所依賴的View)
| | `-- ParticleModel.java(每個(gè)破碎后的粒子的model,顏色、位置、大小等)
| `-- utils
|  `-- UIUtils.java(計(jì)算狀態(tài)欄高度的工具類)
`-- tree.txt

庖丁解牛

下面開(kāi)始每個(gè)類的詳細(xì)分析

本著從簡(jiǎn)到繁、由表及里的原則,詳細(xì)講解每個(gè)類

MainActivity.java

MainActivity.java是測(cè)試動(dòng)效的界面,該Activity內(nèi)部有7個(gè)測(cè)試按鈕。該類做的事情非常單純,就是給每個(gè)View分別綁定click點(diǎn)擊事件,讓View在點(diǎn)擊時(shí)能觸發(fā)爆炸破碎動(dòng)畫(huà)。

/**
 * 說(shuō)明:測(cè)試的界面
 * 作者:Jian
 * 時(shí)間:2017/12/26.
 */
public class MainActivity extends AppCompatActivity {

 /**
  * 加載布局文件,添加點(diǎn)擊事件
  */
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  initViewsClick();
 }

 /**
  * 添加點(diǎn)擊事件的實(shí)現(xiàn)
  */
 private void initViewsClick() {
  // 為單個(gè)View添加點(diǎn)擊事件
  final View title = findViewById(R.id.title_tv);
  title.setOnClickListener(v ->
    new ExplosionField(MainActivity.this).explode(title, null));

  // 為中間3個(gè)View添加點(diǎn)擊事件
  setSelfAndChildDisappearOnClick(findViewById(R.id.title_disappear_ll));
  // 為下面3個(gè)View添加點(diǎn)擊事件
  setSelfAndChildDisappearAndAppearOnClick(findViewById(R.id.title_disappear_and_appear_ll));

  // 跳轉(zhuǎn)到github網(wǎng)頁(yè)的點(diǎn)擊事件
  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添加破碎動(dòng)畫(huà),動(dòng)畫(huà)結(jié)束后,把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添加破碎動(dòng)畫(huà),動(dòng)畫(huà)結(jié)束后,View自動(dòng)出現(xiàn)
  * @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是包含一個(gè)粒子的所有信息的model。advance方法根據(jù)值動(dòng)畫(huà)返回的進(jìn)度計(jì)算出粒子的位置和顏色等信息

/**
 * 說(shuō)明:爆破粒子,每個(gè)移動(dòng)與漸變的小塊
 * 作者:Jian
 * 時(shí)間:2017/12/26.
 */
class ParticleModel {
 // 默認(rèn)小球?qū)捀? static final int PART_WH = 8;
 // 隨機(jī)數(shù),隨機(jī)出位置和大小
 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;
 }

 // 每一步動(dòng)畫(huà)都得重新計(jì)算出自己的狀態(tài)值
 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是動(dòng)畫(huà)類,是一個(gè)值動(dòng)畫(huà),在值動(dòng)畫(huà)每次產(chǎn)生一個(gè)值的時(shí)候,就計(jì)算出整個(gè)爆炸破碎動(dòng)效內(nèi)的全部粒子的狀態(tài)。這些狀態(tài)交由使用的View在渲染時(shí)進(jìn)行顯示。

/**
 * 說(shuō)明:爆炸動(dòng)畫(huà)類,讓離子移動(dòng)和控制離子透明度
 * 作者:Jian
 * 時(shí)間: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();

  // 橫向粒子的個(gè)數(shù)
  int horizontalCount = w / ParticleModel.PART_WH;
  // 豎向粒子的個(gè)數(shù)
  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++) {
    //取得當(dāng)前粒子所在位置的顏色
    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調(diào)用,在View上繪制全部的粒子
 void draw(Canvas canvas) {
  // 動(dòng)畫(huà)結(jié)束時(shí)停止
  if (!isStarted()) {
   return;
  }
  // 遍歷粒子,并繪制在View上
  for (ParticleModel[] particle : mParticles) {
   for (ParticleModel p : particle) {
    p.advance((Float) getAnimatedValue());
    mPaint.setColor(p.color);
    // 錯(cuò)誤的設(shè)置方式只是這樣設(shè)置,透明色會(huì)顯示為黑色
    // mPaint.setAlpha((int) (255 * p.alpha)); 
    // 正確的設(shè)置方式,這樣透明顏色就不是黑色了
    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是真實(shí)執(zhí)行上面ExplosionAnimator。ExplosionField會(huì)創(chuàng)建一個(gè)View并依附在Activity的根View上。

/**
 * 說(shuō)明:每次爆炸時(shí),創(chuàng)建一個(gè)覆蓋全屏的View,這樣的話,不管要爆炸的View在任何位置都能顯示爆炸效果
 * 作者:Jian
 * 時(shí)間: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);
 }

 /**
  * 執(zhí)行爆破破碎動(dòng)畫(huà)
  */
 public void explode(final View view, final AnimatorListenerAdapter listener) {
  Rect rect = new Rect();
  view.getGlobalVisibleRect(rect); //得到view相對(duì)于整個(gè)屏幕的坐標(biāo)
  rect.offset(0, -UIUtils.statusBarHeignth()); //去掉狀態(tài)欄高度

  animator = new ExplosionAnimator(this, createBitmapFromView(view), rect);

  // 接口回調(diào)
  animator.addListener(new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animation) {
    if (listener != null) listener.onAnimationStart(animation);
    // 延時(shí)添加到界面上
    attach3Activity((Activity) getContext());
    // 讓被爆炸的View消失(爆炸的View是新創(chuàng)建的View,原View本身不會(huì)發(fā)生任何變化)
    view.animate().alpha(0f).setDuration(150).start();
   }

   @Override
   public void onAnimationEnd(Animator animation) {
    if (listener != null) listener.onAnimationEnd(animation);
    // 從界面中移除
    removeFromActivity((Activity) getContext());
    // 讓被爆炸的View顯示(爆炸的View是新創(chuàng)建的View,原View本身不會(huì)發(fā)生任何變化)
    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直接得到位圖,那么當(dāng)它設(shè)置背景(backgroud)時(shí),不會(huì)讀取到背景顏色
//  if (view instanceof ImageView) {
//   Drawable drawable = ((ImageView)view).getDrawable();
//   if (drawable != null && drawable instanceof BitmapDrawable) {
//    return ((BitmapDrawable) drawable).getBitmap();
//   }
//  }
  //view.clearFocus(); //不同焦點(diǎn)狀態(tài)顯示的可能不同——(azz:不同就不同有什么關(guān)系?)

  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;
 }

 /**
  * 將創(chuàng)建的ExplosionField添加到Activity上
  */
 private void attach3Activity(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);
 }
}

動(dòng)畫(huà)執(zhí)行時(shí)為什么要?jiǎng)?chuàng)建一個(gè)新View(ExplosionField)

其實(shí)上面的動(dòng)畫(huà)類ExplosionAnimator已經(jīng)實(shí)現(xiàn)了核心功能,直接在原View上使用該動(dòng)畫(huà)應(yīng)該是沒(méi)問(wèn)題的。為什么還要引入一個(gè)ExplosionField類呢?動(dòng)畫(huà)的執(zhí)行為什么不能直接在原本的View上執(zhí)行呢?偏偏要在一個(gè)看似多余的ExplosionField對(duì)象上執(zhí)行呢。

這里就得從Android下View繪制原理來(lái)解釋了:Android下的View都有一個(gè)Bound,在View進(jìn)行measure和layout的時(shí)候,已經(jīng)確定了View的大小和位置,如果要在這個(gè)View上進(jìn)行動(dòng)畫(huà)的話,就會(huì)出現(xiàn)動(dòng)畫(huà)只能在view大小范圍內(nèi)進(jìn)行展現(xiàn)。當(dāng)然了,也不是說(shuō)在原來(lái)View上一定不能實(shí)現(xiàn)這一動(dòng)效,就是相當(dāng)復(fù)雜,要在動(dòng)畫(huà)執(zhí)行過(guò)程中,不斷改變?cè)璙iew的大小和View的屬性等信息,相當(dāng)復(fù)雜。

在性能還行的前提下,要優(yōu)先代碼的整潔度,盡量避免為了優(yōu)化的性能,而舍棄整潔清爽的代碼。一般來(lái)說(shuō),過(guò)度的優(yōu)化,并沒(méi)有給用戶帶來(lái)太多體驗(yàn)上的提升,反而給項(xiàng)目帶來(lái)了巨大的維護(hù)難度。

UIUtils.java

UIUtils是關(guān)于UI的工具類,沒(méi)啥可說(shuō)的

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);
 }
}

結(jié)束

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

分享題目:5分鐘快速實(shí)現(xiàn)Android爆炸破碎酷炫動(dòng)畫(huà)特效的示例
網(wǎng)站地址:http://muchs.cn/article42/ijsehc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗(yàn)、網(wǎng)站設(shè)計(jì)電子商務(wù)、、建站公司面包屑導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名