国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

5分鐘快速實現Android爆炸破碎酷炫動畫特效的示例

2019-10-22 18:18:21
字體:
來源:轉載
供稿:網友

這個破碎動畫,是一種類似小米系統刪除應用時的爆炸破碎效果的動畫。

效果圖展示

先來看下是怎樣的動效,要是感覺不是理想的學習目標,就跳過,避免浪費大家的時間。??

Android,爆炸破碎動畫,爆炸

源碼在這里: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.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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 页游| 襄垣县| 揭西县| 三明市| 喀喇沁旗| 大邑县| 阿鲁科尔沁旗| 盐城市| 锦州市| 紫阳县| 罗定市| 白城市| 曲阳县| 铜陵市| 通江县| 山阴县| 木兰县| 青浦区| 林州市| 建瓯市| 东台市| 肥乡县| 县级市| 南和县| 永宁县| 泸溪县| 法库县| 自贡市| 嘉义县| 东乌| 南岸区| 庐江县| 和硕县| 阿克陶县| 太保市| 南澳县| 高尔夫| 南京市| 通城县| 马关县| 林甸县|