上期文章镇楼:
这可能是第二好的自定义 View 教程之绘制
凯哥的文章确实写的细而好呀,这不,活生生把 面试系列 先放一放,继续讲解我们的动画。
一看就是没看 前面的文章 的。这里就不解释啦。
Android 里面对动画可以进行一些分类,主要分为两类:
由于 「Transtion」 重点在于切换而不是动画,所以我们今天直接忽略。废话不用多说,那么我们就直接讲解属性动画「Property Animation」吧。
现在的项目中的动画 99% 都是用的属性动画,所以我们不讲 View Animation。
这一块比较简单,我们可以直接引用凯哥这里的总结图。(凯哥文章,业界良心,真的很赞。)
从图中可以看到, View 的每个方法都对应了 ViewPropertyAnimator 的两个方法,其中一个是带有 -By 后缀的,例如,
对应了
- View.setTranslationX()
和
- ViewPropertyAnimator.translationX()
这两个方法。其中带有
- ViewPropertyAnimator.translationXBy()
后缀的是增量版本的方法,例如,
- -By()
表示用动画把 View 的
- translationX(100)
值渐变为 100,而
- translationX
则表示用动画把 View 的 translationX 值 渐变地增加 100。
- translationXBy(100)
其中的
可以通过
- ViewPropertyAnimator
得到。
- View.animate()
- /**
- * This method returns a ViewPropertyAnimator object, which can be used to animate
- * specific properties on this View.
- *
- * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
- */
- public ViewPropertyAnimator animate() {
- if (mAnimator == null) {
- mAnimator = new ViewPropertyAnimator(this);
- }
- return mAnimator;
- }
使用方式:
/
- setter
方法;
- getter
创建
- ObjectAnimator.ofXXX()
对象;
- ObjectAnimator
方法执行动画。
- start()
其中特别需要注意的是:
方法需要调用
- setter()
对 View 进行重绘。
- invalidate()
采用的是
- ObjectAnimator
方法。
- ObjectAnimator.ofXXX()
的「XXX」字符串。
- setXXX
方法基本都是通用的。
- ViewPropertyAnimator
方法的时候,你会心生疑惑,这其实就是为了对不限定类型的属性做动画。
- ObjectAnimator.ofObject()
例子也懒得写了,直接用凯哥的。
- public
- class
- SportsView
- extends
- View
- {
- float progress = 0;
- ......
- // 创建 getter 方法
- public
- float
- getProgress
- ()
- {
- return progress;
- }
- // 创建 setter 方法
- public
- void
- setProgress
- (float progress)
- {
- this.progress = progress;
- invalidate();
- }
- @Override
- public
- void
- onDraw
- (Canvas canvas)
- {
- super.onDraw(canvas);
- ......
- canvas.drawArc(arcRectF, 135, progress * 2.7f, false, paint);
- ......
- }
- }
- ......
- // 创建 ObjectAnimator 对象
- ObjectAnimator animator = ObjectAnimator.ofFloat(view, "progress", 0, 65);
- // 执行动画
- animator.start();
当然,这些动画都是可以自由组合的,支持「链式调用」,因为它们返回的都是
。
- ViewPropertyAnimator
比如这样。
- view.animate()
- .scaleX(1)
- .scaleY(1)
- .alpha(1);
而对于
,是不能这么用的。不过你可以使用
- ObjectAnimator
来同时在一个动画中改变多个属性。
- PropertyValuesHolder
- PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);
- PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);
- PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);
- ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)
- animator.start();
从上面的 gif 图可以发现,动画是同步进行的,那要是我们希望依次执行怎么办?比如这样,先放大再平移。
万能的 Android 自然难不倒我们,这样就有了
。
- AnimatorSet
完美地解决了我们上面的疑惑。
- AnimatorSet.playSequentially(Animator... items)
- AnimatorSet animatorSet = new AnimatorSet();
- // 两个动画依次执行
- animatorSet.playSequentially(animator1, animator2);
- animatorSet.start();
其中 「animator1」和「animator2」分别是放大和平移的动画。
翻阅官方源码一看,其实不止
一个方法,除了顺序执行,当然有其他方法,比如:
- playSequentially()
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.play(animator1).before(animator2); // 先执行 1 再执行 2
- animatorSet.playTogether(animator2, animator3); // 2 和 3 同时开始
- animatorSet.start();
类似的处理方案,还很多很多方式,具体你可以查看官方源码。
仅靠这些方法做出来的动画效果说实话已经很炫了,不过我们总是不满足,比如我们设计师想这样怎么办?
图片中先是将进度填到了 100,再降回了实际的值。这利用上面所提到的知识,好像根本没法实现,这效果有意思,我们看看怎么实现。
除了合并多个属性和调配多个动画,你还可以在
的基础上更进一步,通过设置
- PropertyValuesHolder
(关键帧),把同一个动画属性拆分成多个阶段。比如,要实现上面的效果,你只需:
- Keyframe
- // 在 0% 处开始
- Keyframe keyframe1 = Keyframe.ofFloat(0, 0);
- // 时间经过 50% 的时候,动画完成度 100%
- Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100);
- // 时间见过 100% 的时候,动画完成度倒退到 80%,即反弹 20%
- Keyframe keyframe3 = Keyframe.ofFloat(1, 80);
- PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);
- ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder);
- animator.start();
先小结一下,「关于复杂的属性关系来做动画」,就这么三种:
来对多个属性同时做动画;
- PropertyValuesHolder
来同时管理调配多个动画;
- AnimatorSet
的进阶使用:使用
- PropertyValuesHolder
来把一个属性拆分成多段,执行更加精细的属性动画。
- PropertyValuesHolder.ofKeyframe()
实际上不太想说这个
,因为它的使用场景确实不多。这里也不精挑细琢了,大概需要记得:
- ValueAnimator
和
- ViewPropertyAnimator
的内部实现其实都是
- ObjectAnimator
,
- ValueAnimator
更是本来就是
- ObjectAnimator
的子类,它们三个的性能并没有差别。它们的差别只是使用的便捷性以及功能的灵活性。所以在实际使用时候的选择,只要遵循一个原则就行:尽量用简单的。能用 View.animate() 实现就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。
- ValueAnimator
另外,它们还支持
设置动画持续时长以及
- setDuration(long duration)
设置各种「速度设置器」。
- setInterpolator()
「速度设置器」,简而言之就是动画的「速度模型」,这个方法可以让动画按照你想要的方式进行。一般情况我们都直接不设置个,所以下面的了解一下就好,甚至跳过也没事。
简单科普一下提供的「速度模型」,需要看效果的 点击这里。
他娘的,太多啦,就不一一写完了,真没啥用处,用到再说嘛。
可以给动画设置监听器,分别有两种设置方式。
可以看到,两种动画方式设置监听器有一点不同的是一个是 「set」,一个是「add」,但它们的参数都是一致的「AnimatorListener」。
有四个回调方法:
- AnimatorListener
- /**
- * <p>Notifies the start of the animation.</p>
- *
- * @param animation The started animation.
- */
- void onAnimationStart(Animator animation);
- /**
- * <p>Notifies the end of the animation. This callback is not invoked
- * for animations with repeat count set to INFINITE.</p>
- *
- * @param animation The animation which reached its end.
- */
- void onAnimationEnd(Animator animation);
- /**
- * <p>Notifies the cancellation of the animation. This callback is not invoked
- * for animations with repeat count set to INFINITE.</p>
- *
- * @param animation The animation which was canceled.
- */
- void onAnimationCancel(Animator animation);
- /**
- * <p>Notifies the repetition of the animation.</p>
- *
- * @param animation The animation which was repeated.
- */
- void onAnimationRepeat(Animator animation);
从代码中可以很清晰的看出:
- onAnimationStart()
- onAnimationEnd()
- onAnimationCancel()
- onAnimationRepeat()
其中只需要注意两点:
取消的时候,依然会调用
- cancel()
,不过是在
- onAnimationEnd()
之后。
- onAnimationCancel()
/
- setRepeatMode()
或者
- setRepeatCount()
方法执行。
- repeat()
不支持重复。
- ViewProperAnimator
除了上面设置的动画生命周期监听器,我们还有其他的方法,比如
/
- ViewPropertyAnimator.setUpdateListener()
。
- ObjectAnimator.addUpdateListener()
这两个方法虽然名称和可设置的监听器数量不一样,但本质其实都一样的,它们的参数都是
。它只有一个回调方法:
- AnimatorUpdateListener
。
- onAnimationUpdate(ValueAnimator animation)
- /**
- * Implementors of this interface can add themselves as update listeners
- * to an <code>ValueAnimator</code> instance to receive callbacks on every animation
- * frame, after the current frame's values have been calculated for that
- * <code>ValueAnimator</code>.
- */
- public static interface AnimatorUpdateListener {
- /**
- * <p>Notifies the occurrence of another frame of the animation.</p>
- *
- * @param animation The animation which was repeated.
- */
- void onAnimationUpdate(ValueAnimator animation);
- }
当动画的属性更新时(不严谨的说,即每过 10 毫秒,动画的完成度更新时),这个方法被调用。
方法的参数是一个
,
- ValueAnimator
是
- ValueAnimator
的父类,也是
- ObjectAnimator
的内部实现,所以这个参数其实就是
- ViewPropertyAnimator
内部的那个
- ViewPropertyAnimator
,或者对于
- ValueAnimator
来说就是它自己本身。
- ObjectAnimator
还是做个小结,自定义 View 中我们使用属性动画主要分为三种方式:
使用它们的任一个都不会有性能差异,只需记住一个原则,依次越来越难,能用简单的就用简单的。
哦,还有一个最重要的原则就是,盯紧「扔物线凯哥」,加入我们的「HenCoder」大军吧。
做不完的开源,写不完的矫情。欢迎扫描下方二维码或者公众号搜索「nanchen」关注我的微信公众号,目前多运营 Android ,尽自己所能为你提升。如果你喜欢,为我点赞分享吧~
来源: https://juejin.im/post/5a050e9351882531d016db0c