Android 屬性動畫實戰

Android 屬性動畫初戰,經過屬性動畫實現相似於美團外賣購物車消失顯示的動效。git

什麼是屬性動畫?

屬性動畫能夠經過直接更改 View 的屬性來實現 View 動畫。例如:github

  1. 經過不斷的更改 View 的座標來實現讓 View 移動的效果;
  2. 經過不斷的更改 View 的背景來實現讓 View 的背景漸變的效果;
  3. 經過不斷的更改 View 的寬高來實現讓 View 變形的效果;
  4. ...

因而可知,利用屬性動畫幾乎能夠處理任何的涉及到 View 的動畫效果。app

實戰

具體的細節就很少說了,網上相應的教程也很多。這篇博客主要是來實現相似於美團外賣購物車的效果。ide

分析

首先分析購物車動畫具體的細節:在滑動過程當中,「購物車」向右移動,直至一半隱藏到右側;在手指停留在屏幕中時,「購物車」還隱藏在右側;當手指離開屏幕,「購物車」在必定時間後從新移動回來。函數

以上的動畫細節能夠分析出和 RecycleView 的滾動事件息息相關,所以動畫就應該在 RecycleView  的滾動事件中實現。佈局

實現基本佈局

上圖的藍色圖片既是咱們要處理的 View 。動畫

給 RecycleView 加上滾動事件

接下來給 RecycleView 加上滾動事件,滑動或者飛翔時圖片消失,當中止滑動時圖片顯示。this

 1 // 給rv加上滾動事件 2 rv.addOnScrollListener(new RecyclerView.OnScrollListener() { 3     @Override 4     public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { 5         switch (newState) { 6             case RecyclerView.SCROLL_STATE_DRAGGING:// 滾動中 7             case RecyclerView.SCROLL_STATE_SETTLING:// 飛翔中 8                 iv.setVisibility(View.GONE); 9                 break;10             case RecyclerView.SCROLL_STATE_IDLE:// 中止滾動11                 iv.setVisibility(View.VISIBLE);12                 break;13         }14     }15 });

效果圖:3d

實現消失的動畫

根據上面的圖能夠發現觸發時機基本是沒問題了,接下來要作的是讓消失不突兀,加上消失的動畫。調試

消失的實質是 View 的 x 座標從當前位置一直往右直到變爲隱藏了一半,下面讓咱們來實現這個效果:

 1 // 消失動畫的基本屬性(從iv當前的x座標一直到出了屏幕右側一半) 2 disappearAnimator = ValueAnimator.ofFloat(iv.getX(), (float) (Utils.getScreenWidth(this) - iv.getWidth() / 2.0)); 3 disappearAnimator.setDuration(400);// 動畫持續時間 4 disappearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 5     @Override 6     public void onAnimationUpdate(ValueAnimator animation) {// Value更新事件 7         float curValue = (float) animation.getAnimatedValue(); 8         iv.setX(curValue); 9     }10 });11 12 // 給rv加上滾動事件13 rv.addOnScrollListener(new RecyclerView.OnScrollListener() {14     @Override15     public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {16         switch (newState) {17             case RecyclerView.SCROLL_STATE_DRAGGING:// 滾動中18             case RecyclerView.SCROLL_STATE_SETTLING:// 飛翔中19                 disappearAnimator.start();20                 break;21             case RecyclerView.SCROLL_STATE_IDLE:// 中止滾動22                 iv.setVisibility(View.VISIBLE);23                 break;24         }25     }26 });

然而發現實際上動畫是這樣的:

發現他是從最左邊一直移動到了最右邊,與咱們的需求不符。

經調試發現,在 onCreate 的時候 iv 還沒有初始化完畢,所以寬高以及座標都還不能獲取到。因此獲取到的座標以及寬度都是0。

因此能夠在滾動事件中獲取 iv 的座標以及寬高,更改後的代碼以下:

 1 // 給rv加上滾動事件 2 rv.addOnScrollListener(new RecyclerView.OnScrollListener() { 3     @Override 4     public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { 5         // 獲取iv的座標以及寬高 6         if (0 == originX) { 7             originX = iv.getX(); 8             ivWidth = iv.getWidth(); 9         }10 11         switch (newState) {12             case RecyclerView.SCROLL_STATE_DRAGGING:// 滾動中13             case RecyclerView.SCROLL_STATE_SETTLING:// 飛翔中14                 // 消失動畫的基本屬性(從iv當前的x座標一直到出了屏幕右側一半)15                 if (disappearAnimator == null) {16                     disappearAnimator = ValueAnimator.ofFloat(originX, (float) (screenWidth - ivWidth / 2.0));17                     disappearAnimator.setDuration(400);// 動畫持續時間18                     disappearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {19                         @Override20                         public void onAnimationUpdate(ValueAnimator animation) {// Value更新事件21                             float curValue = (float) animation.getAnimatedValue();22                             iv.setX(curValue);23                         }24                     });25                 }26 27                 disappearAnimator.start();28                 break;29             case RecyclerView.SCROLL_STATE_IDLE:// 中止滾動30                 iv.setVisibility(View.VISIBLE);31                 break;32         }33     }34 });

效果以下圖:

實現出現的動畫

既然已經實現了消失的動畫,那出現的動畫也就不難了。出現的實質是 View 的 x 座標從右側一半一直運動到原始位置。

 1 case RecyclerView.SCROLL_STATE_IDLE:// 中止滾動 2     // 出現動畫的基本屬性(從屏幕右側一半到原始位置) 3     if (appearAnimator == null) { 4         appearAnimator = ValueAnimator.ofFloat((float) (screenWidth - ivWidth / 2.0), originX); 5         appearAnimator.setDuration(400);// 動畫持續時間 6         appearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 7             @Override 8             public void onAnimationUpdate(ValueAnimator animation) {// Value更新事件 9                 float curValue = (float) animation.getAnimatedValue();10                 iv.setX(curValue);11             }12         });13     }14 15     appearAnimator.start();16     break;

效果圖以下:

可是發現若是頻繁的滑動暫停的話動畫會衝突,所以須要作一些斷定,若是動畫正在運行則再也不從新開始動畫。改動後的代碼以下:

 1 switch (newState) { 2     case RecyclerView.SCROLL_STATE_DRAGGING:// 滾動中 3     case RecyclerView.SCROLL_STATE_SETTLING:// 飛翔中 4         // 消失動畫的基本屬性(從iv當前的x座標一直到出了屏幕右側一半) 5         if (disappearAnimator == null) { 6             disappearAnimator = ValueAnimator.ofFloat(originX, (float) (screenWidth - ivWidth / 2.0)); 7             disappearAnimator.setDuration(400);// 動畫持續時間 8             disappearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 9                 @Override10                 public void onAnimationUpdate(ValueAnimator animation) {// Value更新事件11                     float curValue = (float) animation.getAnimatedValue();12                     iv.setX(curValue);13                 }14             });15         }16 17         // 若是消失動畫還未開始執行而且iv的位置在原始位置,則執行18         if (!disappearAnimator.isStarted() && originX == iv.getX()) {19             disappearAnimator.start();20         }21         break;22     case RecyclerView.SCROLL_STATE_IDLE:// 中止滾動23         // 出現動畫的基本屬性(從屏幕右側一半到原始位置)24         if (appearAnimator == null) {25             appearAnimator = ValueAnimator.ofFloat((float) (screenWidth - ivWidth / 2.0), originX);26             appearAnimator.setDuration(400);// 動畫持續時間27             appearAnimator.setStartDelay(700);// 延遲時間28             appearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {29                 @Override30                 public void onAnimationUpdate(ValueAnimator animation) {// Value更新事件31                     float curValue = (float) animation.getAnimatedValue();32                     iv.setX(curValue);33                 }34             });35         }36 37         // 若是出現動畫還未開始執行,則執行38         if (!appearAnimator.isStarted()) {39             appearAnimator.start();40         }41         break;42 }

可是發現仍是會有衝突,經檢測,發現是出現動畫和消失動畫互相干擾的緣故。當滑動已暫停但出現動畫還未執行完畢,此時從新滑動會觸發消失動畫。

所以須要給出現動畫加一個延遲,而且若是處於非暫停狀態須要取消出現動畫。(也許美團外賣暫停一段時間纔開始出現的緣由就是防止用戶不停的滑動暫停吧。)

修改後的代碼以下:

case RecyclerView.SCROLL_STATE_DRAGGING:// 滾動中case RecyclerView.SCROLL_STATE_SETTLING:// 飛翔中    // 消失動畫的基本屬性(從iv當前的x座標一直到出了屏幕右側一半)
    if (disappearAnimator == null) {
        disappearAnimator = ValueAnimator.ofFloat(originX, (float) (screenWidth - ivWidth / 2.0));
        disappearAnimator.setDuration(400);// 動畫持續時間
        disappearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override            public void onAnimationUpdate(ValueAnimator animation) {// Value更新事件
                float curValue = (float) animation.getAnimatedValue();
                iv.setX(curValue);
            }
        });
    }    // 若是此時出現動畫已開始,則取消
    if (appearAnimator != null && appearAnimator.isStarted()) {
        appearAnimator.cancel();
    }    // 若是消失動畫還未開始執行而且iv的位置在原始位置,則執行
    if (!disappearAnimator.isStarted() && originX == iv.getX()) {
        disappearAnimator.start();
    }    break;

最終效果如圖所示:

總結
  • 若是要實現其餘的效果,例如淡入淡出等同理就能夠實現;
  • 多個動畫對統一個 View 作變換時必定要注意動畫之間的衝突;
  • 屬性動畫+函數方程能夠實現一些豐富多變的效果,待研究;
  • 本文的實現仍是比較簡陋,最好的效果是動畫能夠被打斷,因爲比較麻煩,因此沒有實現。
Github地址: 屬性動畫初戰

 

你們若是有什麼疑問或者建議能夠經過評論或者郵件的方式聯繫我,歡迎你們的評論~

相關文章
相關標籤/搜索