前言
在前面的文章中我们讲述了Android动画之视图动画学习了怎么对一个view实现动画,可以实现动画包括平移,旋转,缩放,渐变和帧动画,这一篇我们来学习一个新的动画实现方式。
属性动画
为什么需要属性动画?
Android在3.0引入了属性动画,既然之前已经有属性动画了,为什么还要引入属性动画呐?视图动画不好用吗?还是视图动画不能够实现功能?随着系统的迭代更新,和设计的发展,以前做app都是比较简单的,但是现在的app越来越复杂,功能越来越多,动画效果也更加的复杂,更阿基要求实时性和界面的绚丽,因此老的系统很多实现都已经改变,对新的功能也力不从心,所以才引入属性动画来改善动画的效果, 主要有如下原因:
1,视图动画是非交互动画,仅仅只是改变了显示位置
2,视图动画种类有限,复杂的效果力不从心
3,视图动画调用onDraw来重绘界面,耗费资源
4,视图动画只能针对view实现动画,对非view不能实现
我们分别解释一下上面的四点:
视图动画是非交互动画,仅仅只是改变了显示位置
我们在上一章讲述了视图动画是非交互动画,这个虽然不是一个很大的缺陷,但是在真的需要交互的时候就力不从心了,要实现额外的代码才能实现其效果,
视图动画种类有限,复杂的效果力不从心
其次在现阶段很多app都有非常复杂的效果,仅仅依靠视图动画的组合往往很难实现,就算实现了效果,代码也非常的多,可能难以维护。
视图动画调用onDraw来重绘界面,耗费资源
onDraw来返回重绘界面,这样就导致耗费资源和性能,流畅性是一个app非常重要的体验,虽然单一一个动画不能特别明显的拖慢性能,但是各个地方差值综合就会导致体验很差
视图动画只能针对view实现动画,对非view不能实现
视图动画的种类仅仅只有四种,并且只能针对view实现,如果只是针对一个view的背景颜色,或者其他的属性来实现动画,就不能实现。
属性动画的特点
我们在上面大书特书了视图动画的不足,那是不是属性动画就完全弥补了视图动画的不足?答案是的:
1,属性动画真正改变的是对象的属性
2,属性动画是可以交互的
3,属性动画针对view与非view都能实现动画
4,如果属性不存在,还可以自定义属性来实现动画
视图动画的使用场景
虽然属性动画有这样那样的优点,是不是视图动画我们就抛诸脑后,不再使用?是,也不是?如果你新开发一个app,并且不是支持3.0一下的版本,那最好是直接使用属性动画,那什么时候使用视图动画?
1,现有代码已经实现效果
2,视图动画能实现所需要的效果
3,视图动画只需要更少的代码来实现
代码结构
- Animator
- ValueAnimator
- ObjectAnimator
- TimeAnimator
- AnimatorSet
- ValueAnimator
这里我们主要注意一下视图动画与属性动画的异同。代码结构很类似,但是根节点是不一样的,视图动是Animation,属性动画是Animator,视图动画有四个子类,平移,旋转,缩放,渐变,这里完全用ObjectAnimator来代替了。
动画实现方式
xml实现
在res/animator下定义动画文件(ValueAnimator-animator,ObjectAnimator
–objectAnimator, AnimatorSet-set ) 代码中采用AnimatorInflater().
loadAnimator()加载
代码实现
采用ObjectAnimator或者ValueAnimator实现
动画实现
ObjectAnimator
我们上面讲述了两种动画的实现方式。这里我们先讲述一下代码实现,我们先来看看在视图动画的一个截图,当时实现了平移,旋转,缩放,渐变,那这里我们用属性动画来实现一遍:
在实现这四种动画之前,我们先来讲解一下使用方式,我们先来看看可以使用的api有哪些?
我们从图片可以到,of类型的函数有很多种,有ofArgb,ofFloat,ofInt等,这里有这么多api,那我们使用哪一个?这里我们主要使用ofFloat,因为这个函数是从3.0引入的,这个函数比较通用,其他的函数有的是4.0引入的,5.0引入的。因此这里我们主要讲解ofFloat:
ObjectAnimator.ofFloat(Object target, String propertyName, float... values)
各个属性的解释如下:
1,target作用的对象
2,propertyName作用的属性,需要有public的setName函数
3,values可变参数,表示动画执行的一组值
渐变
private void alpha() {
ObjectAnimator alpha = ObjectAnimator.ofFloat(love, "alpha", 1.0f, 0.0f);
alpha.setDuration(1000);
alpha.start();
}
这里使用的属性是alpha,1.0f表示完全不透明,0.0f表示完全透明。
平移
private void translate() {
ObjectAnimator translate = ObjectAnimator.ofFloat(love, "translationX", 200f);
translate.setDuration(1000);
translate.start();
}
这里使用的是translationX属性,只平移了x轴,Y轴不做变化。
缩放
private void scale() {
ObjectAnimator scaleX = ObjectAnimator.ofFloat(love, "scaleX", 1.0f, 2.0f);
scaleX.setDuration(1000);
scaleX.start();
ObjectAnimator scaleY = ObjectAnimator.ofFloat(love, "scaleY", 1.0f, 2.0f);
scaleY.setDuration(1000);
scaleY.start();
}
这里使用了scaleX与scaleY属性,1.0f表示原始大小,2.0f表示放大一倍。默认缩放为中心缩放。
旋转
private void rotate() {
ObjectAnimator rotate = ObjectAnimator.ofFloat(love, "rotation", 0f, 360f);
rotate.setDuration(1000);
rotate.start();
}
这里使用的是rotation属性,旋转了360度,默认的旋转中心为视图中心。
上面我使用了一系列属性,我怎么知道哪些属性可以用哪些属性不能用。这里我们去查找对应view所包含的属性就知道了。
没有对应属性
上面我们都是使用已知的属性,如果一个view我们想要实现的效果没有对应的属性怎么办?
直接添加set函数
包装原有类,添加set函数
采用ValueAnimator实现
这里我们解释以下上面的情况,第一种如果你有权限,或者是自定义的view,如果没有对应属性,我们可以手动添加对应的属性,第二种情况,我们可以包装现有类,添加对应的属性,第三种我们可以采用ValueAnimator来实现,这里我们来包装一下原有类,后面我们会讲解ValueAnimator的实现。
我们就以上述缩放效果来演示,上面我们使用了两个属性scaleX,scaleY来缩放一个view,这里我们用一个属性scale属性来缩放,但是ImageView没有缩放这个属性,那怎么办?
public class WrapperView {
public ImageView target;
private float scale;
public WrapperView(ImageView target) {
this.target = target;
}
public float getScale() {
return scale;
}
public void setScale(float scale) {
this.scale = scale;
target.setScaleX(scale);
target.setScaleY(scale);
}
}
我们包装对应的imageview,给他添加对应的scale属性,之后在代码中使用。动画代码如下:
private void addProperty() {
WrapperView view = new WrapperView(love);
ObjectAnimator scale = ObjectAnimator.ofFloat(view, "scale", 1.0f, 2.0f);
scale.setDuration(1000);
scale.start();
}
这里我们使用的属性就是上述添加的scale属性。这样我们就实现了缩放的效果。
组合动画
我们在视图动画时讲过,动画有并行,串行,属性动画也有这些组合。那我们接下来就讲述一下这些效果。
并行动画
并行动画,就是说多个动画同时执行,比如我们在旋转的同时缩放一个view,这里主要有三种方式实现:
1:多个ObjectAnimator实现
private void togetherAnim1() {
ObjectAnimator rotate = ObjectAnimator.ofFloat(love, "rotation", 0f, 360f).setDuration(1000);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(love, "scaleX", 1.0f, 2.0f).setDuration(1000);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(love, "scaleY", 1.0f, 2.0f).setDuration(1000);
// rotate.start();
// scaleX.start();
// scaleY.start();
AnimatorSet set = new AnimatorSet();
set.playTogether(rotate, scaleX, scaleY);
set.setDuration(1000);
set.start();
}
这里我们定义了三个ObjectAnimator,之后用AnimatiorSet来进行执行。调用playTogether来同时执行这个三个动画,还有一种方式是,可以分别start三个动画也能实现效果。
这里有人可能会有疑问,连续三个start那不应该是串行的吗?怎么会是并行的呐?其实这里的start,动画并没有立即开始执行,而是等待下一个刷新时间戳过来才执行的。
2:一个ObjectAnimator实现
private void togetherAnim2() {
PropertyValuesHolder rotate = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 2.0f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 2.0f);
ObjectAnimator together = ObjectAnimator.ofPropertyValuesHolder(love, rotate, scaleX, scaleY);
together.setDuration(1000);
together.start();
}
这里我们使用PropertyValuesHolder来实现,定义三个PropertyValuesHolder,它与ObjectAnimator的区别是没有target属性,之后将PropertyValuesHolder放置到ObjectAnimator里面,之后start ObjectAnimator动画。
3:采用ViewPropertyAnimator实现
private void togetherAnim3() {
love.animate().rotation(360f).scaleX(2.0f).scaleY(2.0f).setDuration(1000).start();
}
该方式系统已经实现了一个帮助方法,animate,之后可以链式的执行各个属性。该方式代码量是最少了的。
串行动画
前面已经实现了并行,我们调用了playTogether,这里我们只要换成另外一种方法就可以实现串行。
private void sequenceAnim() {
ObjectAnimator rotate = ObjectAnimator.ofFloat(love, "rotation", 0f, 360f).setDuration(1000);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(love, "scaleX", 1.0f, 2.0f).setDuration(1000);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(love, "scaleY", 1.0f, 2.0f).setDuration(1000);
AnimatorSet set = new AnimatorSet();
set.playSequentially(rotate, scaleX, scaleY);
set.setDuration(1000);
set.start();
}
只需要将playTogether换成为playSequentially就可以了。
排列多个动画
前面我们实现了并行与串行动画,但是如果一个动画既有串行又有并行怎么办?代码如下:
private void compositeAnim() {
ObjectAnimator translateY = ObjectAnimator.ofFloat(love, "translationY", -200f).setDuration(1000);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(love, "scaleX", 1.0f, 2.0f).setDuration(1000);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(love, "scaleY", 1.0f, 2.0f).setDuration(1000);
ObjectAnimator rotate = ObjectAnimator.ofFloat(love, "rotation", 0f, -45f).setDuration(1000);
AnimatorSet set = new AnimatorSet();
set.play(scaleX).with(scaleY).with(rotate);
set.play(translateY).after(scaleX);
set.start();
}
这里还是采用AnimatorSet来实现,with表示同时执行动画。before方法,before里面的代码后执行,after方法,after里面的动画先执行。
ValueAnimator
前面我们已经讲解了ObjectAnimator,我们前面的代码结构中还有ValueAnimator,那ValueAnimator是什么呐?
1,ValueAnimator是一个数值发生器
2,ValueAnimator不作用于任何属性
3,需要在onAnimationUpdate获取当前时间点发生的值
我们解释一下上面的要点,首先ValueAnimator是一个数值发生器,就是他只产生数值,不对任何属性起作用,因此他与ObjectAnimator不同,他不能产生动画,动画需要我们手动来处理。处理的方式就是在onAnimationUpdate中获取产生的值。
那我们就来用ValueAnimator来实现一个动画,我们实现一个倒计时的动画:
private void countDown() {
ValueAnimator countDown = ValueAnimator.ofInt(10, 0);
countDown.setDuration(10000);
countDown.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
textView.setText("" + animation.getAnimatedValue());
}
});
countDown.start();
}
这里我们用的ofInt函数,因为倒计时是整数,所以这里用ofInt,我们在对动画设置执行时长,最后在回调中获取产生的值,animation.getAnimatedValue()就是当前产生的值。我们使用这个产生的值,实现倒计时动画效果。
动画监听
我们已经学习了怎么用ObjectAnimator和ValueAnimator来处理动画,但是如果需要监听动画的开始和结束又怎么办?
我们可以添加监听:
private void alpha() {
ObjectAnimator alpha = ObjectAnimator.ofFloat(love, "alpha", 1.0f, 0.0f);
alpha.setDuration(1000);
alpha.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
Toast.makeText(ObjectAnimationActivity.this, "start", Toast.LENGTH_LONG).show();
;
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
alpha.start();
}
我们可以对动画添加Animator.AnimatorListener监听,有四个回调函数,这样我们就可以在动画开始,结束进行处理。
不过上诉有四个回调,我们往往是不需要处理这么多回调,仅仅只是想处理需要关注的回调,这种情况有解吗?我们可以采用如下的方式进行处理:
private void translate() {
ObjectAnimator translate = ObjectAnimator.ofFloat(love, "translationX", 200f);
translate.setDuration(1000);
translate.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//TODO
}
});
translate.start();
}
对动画设置AnimatorListenerAdapter,这里我们只需要关注需要关注的回调。
还有一种监听方式我们前面已经使用过了。就是ValueAnimator中添加监听,我们可以添加ValueAnimator.AnimatorUpdateListener监听,在onAnimationUpdate回调中进行处理。
插值器
在视图动画中,我们已经讲解过插值器,这里的插值器与视图动画中的插值器没有什么区别。因此就不在进行讲述了。
总结
这里我们已经讲述完成了属性动画的实现,还有其他的属性没有实现,在接下来的一篇中我们继续讲述剩下的属性。