android动画分为三种 View动画 帧动画 属性动画 其中帧动画也属于View动画 不过和常见的View旋转、平移、缩放、透明度的表现形式不同
7.1.1View动画的种类
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX9UFVNh3YE90MZpWTmZEWjZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TM2gzMwMDN4EjMyQDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
使用View动画首先需要创建xml文件这个文件的路径为res/anim/filename.xml ,描述文件有固定的语法
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="true">
<alpha
android:fromAlpha="10.0"
android:toAlpha="10.0" />
<scale
android:fromXScale="10dp"
android:fromYScale="10dp"
android:pivotX="10"
android:pivotY="10"
android:toXScale="10dp"
android:toYScale="10dp" />
<translate
android:fromXDelta="10"
android:fromYDelta="10"
android:toXDelta="10"
android:toYDelta="10" />
<rotate
android:fromDegrees="10"
android:pivotX="10"
android:pivotY="10"
android:toDegrees="10" />
</set>
从上边的语法来看,View的动画既可以是单个动画也可以由一系列的动画组成
<set>标签表示动画集合,对应AnimationSet类它包含很多个类有两个属性:
android:interpolator :表示动画集合所使用的差值器,差值器影响动画的速度,比如非匀速动画就需要差值器来制作动画的过程,这个属性可以不指定,默认为加速减速差值器
android:shareInterpolator:表示集合中的动画是否合计和使用一个差值器,如果集合不指定差值器,那么子动画就需要单独的指定所需要的差值器了
< translate>标签表示平移动画:
- android:fromXDelta
表示x的起始值,比如0
- android:fromYDelta
表示y的结束值,比如100
- android:toXDelta
表示y的起始值
- android:toYDelta
表示y的结束值
< scale>标签表示的是缩放动画
android:fromXScale
水平方向缩放的起始值,比如0.5
android:fromYScale
竖直方向缩放的起始值
android:pivotX
缩放轴点的x坐标,它会影响缩放的效果
android:pivotY
缩放轴点的y坐标,它会影响缩放的效果
android:toXScale
水平方向缩放的结束值,比如1.2
android:toYScale
竖直方向缩放的起始值
<routate>表示动画的旋转,对应RotateAnimation,它可以使View具有旋转的动画效果
android:fromDegrees
旋转开始的角度,比如0
android:toDegrees
旋转结束的角度,比如180
android:pivotX
旋转轴点的x
android:pivotY
旋转轴点的y
在旋转中也有轴的概念,他也会影响到旋转的效果,轴点扮演者旋转轴的角色,view围绕着轴点进行旋转,默认情况下在view的中心,考虑到一种情况,view围绕自己的中心,和围绕左上角进行90度显然是不同的轨迹
< alpha>表示透明动画。对应的AlphaAnimation
- android:fromAlpha
表示透明度的起始值,比如0.1
- android:toAlpha
上面都只是很简单的介绍了XM格式,具体的使用方法还是看文档,我们还有一些常用的属性如下
- android:duration
动画的时间
- android:fillAfter
动画结束之后是否停留在结束的位置
除了在xml中创建动画 还可以通过代码实现 但是对于View动画来说建议采用XML来定义动画,这是因为XML的可读性更好,淡然在实际开发中使用xml也是第一选择
7.1.2自定义View动画
除了系统提供的四种View动画外,我们还可以自定义View动画自定义动画,在实际开发过程中基本上用不到
7.1.3帧动画
帧动画就是顺序播放的一组图片,系统提供了一个AnimationDrawable类来实现帧动画
在xml中定义
7.2View动画的使用场景
view动画还可以在一些特殊的场景使用 比如在ViewGroup中可以控制子元素的出场效果,实现不同activity之间的切换效果
7.2.1 LayoutAnimation 作用于ViewGroup为ViewGroup制定一个动画这样他的子元素出场的时候就具有这种动画了,这种效果常被用于listview中
7.2.2 Activity的切换效果
启动一个
startActivity(new Intent(MainActivity.this,OneActivity.class));
//这是activity的跳转动画
overridePendingTransition(R.anim.animation,R.anim.anim_layout);
退出一个
@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(R.anim.animation, R.anim.anim_layout);
}
7.3属性动画
属性动画可以对任何队形做动画们甚至没有对象也可以,属性动画效果得到加强,不再像View动画支持4种属性动画有ValueAnimator,ObjectAnimator,AnimatorSet; ObjectAnimator继承自ValueAnimator,而AnimatorSet可以定义一组动画一般我们都用ObjectAnimator来声明一个ValueAnimator在ObjectAnimator声明属性变化值。
属性动画可以对任意对象的属性进行动画,而不仅仅是View 动画的默认时间是300 默认帧率是10
7.3.1使用属性动画
(1)改变一个对象的translationY属性,让其沿着Y轴向上平移一个时间,该动画在默认的时间完成,
ObjectAnimator.ofFloat(iv_icon, "translationY", -iv_icon.getHeight()).start();
(2)改变一个对象的背景颜色值,典型的就是改变view的背景,下面的动画是让view的背景从0xffff8080到0xff8080ff,动画会无限循环和反转
ValueAnimator valueAnimator =
ObjectAnimator.ofInt(ll_content, "backgroundColor", 0xFFFF8080, 0xFF8080FF);
valueAnimator.setDuration(3000);
valueAnimator.setEvaluator(new ArgbEvaluator());
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
valueAnimator.start();
(3)动画集合,5s内对view的旋转,平移,缩放和透明度进行改变
实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:
- after(Animator anim) 将现有动画插入到传入的动画之后执行
- after(long delay) 将现有动画延迟指定毫秒后执行
- before(Animator anim) 将现有动画插入到传入的动画之前执行
- with(Animator anim) 将现有动画和传入的动画同时执行
好的,有了这四个方法,我们就可以完成组合动画的逻辑了,那么比如说我们想要让TextView先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:
1 2 3 4 5 6 7 | |
可以看到,这里我们先是把三个动画的对象全部创建出来,然后new出一个AnimatorSet对象之后将这三个动画对象进行播放排序,让旋转和淡入淡出动画同时进行,并把它们插入到了平移动画的后面,最后是设置动画时长以及启动动画。
当然也可以直接使用playTogether方法将所有的动画操作放入一个集合中
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(iv_icon, "rotationX", 0, 360), 中心x轴进行3d旋转
ObjectAnimator.ofFloat(iv_icon, "rotationY", 0, 180),中心y轴进行3d旋转
ObjectAnimator.ofFloat(iv_icon, "rotation", 0, -90),中心进行平面旋转
ObjectAnimator.ofFloat(iv_icon, "translationX", 0, 90),x轴进行平移
ObjectAnimator.ofFloat(iv_icon, "translationY", 0, 90),y轴进行平移
ObjectAnimator.ofFloat(iv_icon, "scaleX", 0, 1.5f),,x轴进行缩放
ObjectAnimator.ofFloat(iv_icon, "scaleY", 0, 0.5f),y轴进行缩放
ObjectAnimator.ofFloat(iv_icon, "alpha", 0, 2.5f, 1) 透明度变化
);
set.setDuration(3000).start();
属性动画的各个参数是比较好理解的,我们简单来说下他们之间的含义
propertyName:表示属性动画作用对象的属性的名称
duration:表示动画的时长
valueFrom:表示属性的起始值
valueTo:表示属性的结束值
startOffset:表示动画的延迟时间,当动画开始后,需要延迟多少毫秒才会真正的播放
repeatCount:表示动画的重复次数
repeatMode:表示动画的重复模式
valueType:表示propertyName有两个属性有int和float两个可选项,分别表示属性的类型,和浮点型,另外,如果所制定的是颜色类型,那么就不需要指定propertyName,系统会自动对颜色类型进行处理
实际开发中一般使用代码动态创建属性动画
ValueAnimator、 ObjectAnimator、 AnimatorSet都可以使用addListener添加监听事件,生成四个方法,如果你觉得每次实现四种方法,太磨叽,那么AnimatorListenerAdapter也可以实现你的逻辑,他可以实现你想实现的某一个方法。
7.3.4 对任意的属性做动画
这里最一个需求,就是给buttion设置一个动画,让他的宽度从当前的变成500px,这个可以用view动画来搞定,但是你仔细想想,view不能对宽高变化,所有我么可以使用属性动画,
我们会发现button确实拉伸了
但是书中却说不能改变
看下大佬的分享
下面对属性动画的原理:属性动画要求动画作用在对象提供的get/set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画效果多次去set,每次传递的set方法的值都不一样,确切来说是随着时间的时间推移,所传递的值越来越接近最终值,总结一下,我们对object的属性abc做动画,如果想让动画生效,要同时满足两个条件:
(1)object必须要提供set方法,如果动画的时候没有传递初始值,那么我们还要提供get方法,因为系统要去取abc的属性(如果这条不满意,程序直接Crash)
(2)object的set方法对abc所做的改变必须通过某种方法反应,比如带来UI的改变(如果这条不满足,动画无效果但是不会Crash)
以上条件缺一不可,那么为什么我们对button的width属性做动画没有效果,这是因为button内部虽然提供了get/set方法,但是这个set方法并不是改变视图大小,他是textview新添加的方法,view是没有这个setWidth方法的,由于button继承了textview,所有button也就有了set方法,下面看一下这个get/get的源码
@android.view.RemotableViewMethod
public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS;
requestLayout();
invalidate();
}
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
从上述的源码可以看出,get的确是获取view的宽度,而set是textview的专属方法,他的作用不是设置view的宽度,而是设置textview的最大宽度和最小宽度,这个和textview的宽度不死一个东西,具体来说,textview的宽度对应XML中的android:layout_width,而textview还有一个属性android:width,这个就对应了setwidth,总之textview和button的set/get干的不是同一件事,通过set无法改变控件的宽度,所以对width做属性动画没有效果,对于属性动画的两个条件来说,本例中的动画只满足了第一个条件,我们有三个解决办法:
给你的对象增加set/get方法,前提是你有权限的话
用这个类来包装原始对象,间接提供get/set方法
采用ValueAnimator,监听动画过程自己去实现
我们来具体的实现下这三个解决办法
1.给你的对象增加set/get方法,前提是你有权限的话
这个的意思很好理解,如果你有权限的话,加个set/get方法就搞定了,但是很多时候我们没有权限去这么做,比如本文开头所提到的问题,你无法给button加上一个合乎要求的setwidth方法,因为这个是Android SDK内部实现的,这个方法很简单,但是往往是不可行的,这里就不对其进行更多的分析了
2.用这个类来包装原始对象,间接提供get/set方法
private void performAnimate() {
ViewWrapper viewWrapper = new ViewWrapper(btn);
ObjectAnimator.ofInt(viewWrapper, "width", 500).setDuration(1000).start();
}
private static class ViewWrapper {
private View mTarget;
public ViewWrapper(View mTarget) {
this.mTarget = mTarget;
}
public int getWidth() {
return mTarget.getLayoutParams().width;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
上述代码在1000ms中宽度增加到500,为了达到这个效果我们写了一个包装类去提供方法,这样也就完美的实现了
3.采用ValueAnimator,监听动画过程自己去实现
首先说下什么是ValueAnimator,ValueAnimator本身不作用于任何对象,也就是说直接使用它没有任何的效果,他可以对一个值做动画,然后我们监听这个过程,在过程中修改我们对象的属性值,这样就相当于我们的对象做了动画,下面我们用例子来说明
private void performAnimator(final View target, final int start, final int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//持有一个IntEvaluator对象,方便下面估值的时候使用
private IntEvaluator mEvaluator = new IntEvaluator();
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获得当前动画的进度值,整形1-100之间
int currentValue = (int) animation.getAnimatedValue();
//获得当前进度占整个动画之间的比例,浮点0-1之间
float fraction = animation.getAnimatedFraction();
//直接使用整形估值器,通过比例计算宽度,然后再设置给按钮
target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
target.requestLayout();
}
});
valueAnimator.setDuration(5000).start();
}
上面的代码的效果和刚才的viewwrapper是一样的,关于ValueAnimator还要再说一下,拿上来的例子来说,他会在5s内将一个数1变成100,然后动画的每一帧会回调的每一帧onAnimationUpdate方法,在这个方法里,我们可以获取当前的值和占用的比例我们可以计算出宽度是多少,比如时间过去了一半,当前值是50,比例是0.5,假设起始值为100,最终是500px,那么500-100=400,所有这个时候乘以0.5=200,这些都是内部实现,我们不用自己写,直接用。
7.3.5 属性动画的工作原理