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地址: 属性动画初战

 

你们若是有什么疑问或者建议能够经过评论或者邮件的方式联系我,欢迎你们的评论~

相关文章
相关标签/搜索