参考:
https://mp.weixin.qq.com/s/62jNAKdxpS_dOfzfYxZOww
练习github地址:https://github.com/fandazeng/ViewDemo
简介
属性动画是 API11 新加入的特性,和 View 动画不同,它可以对任何对象做动画,甚至还可以没有对象,动画默认时间间隔 300ms,默认帧率 10ms/ 帧。其可以达到的效果是:在一个时间间隔内完成对对象从一个属性值到另一个属性值得改变。常用属性动画类 ValueAnimator 、 ObjectAnimator 和 AnimationSet ,其中ObjectAnimator 继承于ValueAnimator。
1.1 xml 实现方式
它的 xml 语法如下:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering=["sequentially"|"together"]>
<objectAnimator
android:propertyName="string"
android:duration="int"
android:valueFrom="float|int|color"
android:valueTo="float|int|color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["restart"|"reverse"]
android:valueType=["colorType"|"intType"]>
</objectAnimator>
<animator
android:duration="int"
android:valueFrom="float|int|color"
android:valueTo="float|int|color"
android:startOffset="int"
android:repeatCount="int"
android:repeatMode=["restart"|"reverse"]
android:valueType=["colorType"|"intType"]>
</animator>
...
</set>
下面介绍一下各属性名称的含义。属性动画的核心类有3个类,AnimatorSet,ObjectAnimator 以及ValueAnimator 。
<set>
标签对应 AnimatorSet ,<set>
标签的 ordering 属性有两个候选值”sequentially”|”together”,分别表示<set>
标签内的动画是按照前后顺序播放和同时播放。默认是 “together” 。<animatior>
对应 ValueAnimator ,属性如下:
- android:duration:表示动画的时长
- android:valueFrom:表示属性的起始值
- android:valueTo:表示属性的结束值
- android:startOffset:表示动画的延迟时间,动画开始后,需要延迟多少毫秒后才会真正播放该动画
- android:repeatCount:表示动画的重复次数,默认值是0,为-1时,表示无限循环。
- android:repeatMode:表示动画的重复播放模式,restart表示动画每次都是重新开始播放,reverse表示动画第1次播放完毕后,第2次会逆向播放,第3次又从头开始播放,以此类推。
<objectAnimator>
对应 ObjectAnimator 。因为 ObjectAnimator 继承于 ValueAnimator ,所以拥有 ValueAnimator 所有属性。除外还有自己的属性,如下:
- android:propertyName:表示属性动画作用对象的属性名称
- android:valueType:表示android:propertyName的值的类型,分为intType,和floatType,分别代表整型数值和浮点型数值,若android:propertyName指定的属性表示的是颜色,那么无需指定android:valueType,系统会自动适配
具体 xml 示例:
xml 实现的属性动画需要放在 res/animator 目录下,没有则新建一个。
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:propertyName="alpha"
android:repeatCount="-1"
android:repeatMode="reverse"
android:startOffset="200"
android:valueFrom="0.0"
android:valueTo="1.0"
android:valueType="floatType" />
代码调用如下:
//点击播放动画事件
mObjectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(getContext(), R.animator.test_property_animator);
mObjectAnimator.setTarget(mMusicIcon);
mObjectAnimator.start();
1.2 代码实现方式
mObjectAnimator = ObjectAnimator.ofFloat(mMusicIcon, "alpha", 0, 1);
mObjectAnimator.setStartDelay(200);
mObjectAnimator.setDuration(500);
mObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
mObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
在实际开发中建议采用代码来实现属性动画,代码实现较简单,而有时候属性的起始值是无法提前确定的,需要在代码运行中动态变化来确定。
效果如下:
ValueAnimator
整个属性动画机制当中最核心的一个类。
mValueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(500);
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Log.d("ValueAnimator", animation.getAnimatedValue() + "");
}
});
2.1 transLate 动画
xml 文件如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:anim/linear_interpolator"
android:repeatMode="reverse"
android:shareInterpolator="true">
<translate
android:fromXDelta="50%p"
android:fromYDelta="50%p"
android:repeatCount="-1"
android:toXDelta="300"
android:toYDelta="300" />
</set>
代码实现如下:
// 如果是Animation.RELATIVE_TO_PARENT 或 Animation.RELATIVE_TO_SELF ,
则值要用百分比 0% -100%,默认是Animation.ABSOLUTE
mAnimation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT,0.5f,Animation.RELATIVE_TO_PARENT,
0.2f,Animation.RELATIVE_TO_PARENT, 0.5f,Animation.RELATIVE_TO_PARENT, 0.2f);
mAnimation.setRepeatCount(Animation.INFINITE);
mAnimation.setDuration(2000);
mAnimation.setRepeatMode(Animation.REVERSE);
mAnimation.setInterpolator(new LinearInterpolator());
效果如下:
2.2 alpha 动画
xml 文件如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:interpolator="@android:anim/linear_interpolator"
android:repeatMode="reverse"
android:shareInterpolator="true">
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:repeatCount="-1"/>
</set>
代码实现如下:
mAnimation = new AlphaAnimation(1, 0);
...
效果如下:
2.3 scale 动画
xml 文件如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:anim/linear_interpolator"
android:repeatMode="reverse"
android:shareInterpolator="true">
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromYScale="0.5"
android:fromXScale="0.5"
android:toXScale="1.5"
android:repeatCount="-1"
android:toYScale="1.5" />
</set>
代码实现如下:
mAnimation = new ScaleAnimation(0.5f, 1.5f, 0.5f, 1.5f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
...
效果如下:
2.4 rotate 动画
xml 文件如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:anim/linear_interpolator"
android:repeatMode="reverse"
android:shareInterpolator="true">
<rotate
android:fromDegrees="90"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:toDegrees="360" />
</set>
代码实现如下:
mAnimation = new RotateAnimation(90f, 360f, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
...
效果如下:
2.5 AnimationSet 动画集合
AnimationSet继承自Animation,是上面四种的组合容器管理类,没有自己特有的属性,他的属性继承自Animation,所以特别注意,当我们对set标签使用Animation的属性时会对该标签下的所有子控件都产生影响。
xml 文件如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator"
android:repeatMode="reverse"
android:duration="1000"
android:shareInterpolator="true">
<translate
android:fromXDelta="50%"
android:fromYDelta="50%"
android:repeatCount="-1"
android:toXDelta="300"
android:toYDelta="300" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:repeatCount="-1"/>
<scale
android:pivotX="50%"
android:pivotY="50%"
android:fromYScale="0.5"
android:fromXScale="0.5"
android:toXScale="1.5"
android:repeatCount="-1"
android:toYScale="1.5" />
<rotate
android:fromDegrees="90"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:toDegrees="360" />
</set>
代码实现如下:
mTranslateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_PARENT,
0.2f,Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_PARENT, 0.2f);
mTranslateAnimation.setRepeatCount(Animation.INFINITE);
mAlphaAnimation = new AlphaAnimation(1, 0);
mAlphaAnimation.setRepeatCount(Animation.INFINITE);
mScaleAnimation = new ScaleAnimation(0.5f, 1.5f, 0.5f, 1.5f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mScaleAnimation.setRepeatCount(Animation.INFINITE);
mRotateAnimation = new RotateAnimation(90f, 360f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
mRotateAnimation.setRepeatCount(Animation.INFINITE);
mAnimationSet = new AnimationSet(true);
mAnimationSet.addAnimation(mTranslateAnimation);
mAnimationSet.addAnimation(mRotateAnimation);
mAnimationSet.addAnimation(mScaleAnimation);
mAnimationSet.addAnimation(mAlphaAnimation);
mAnimationSet.setDuration(1000);
mAnimationSet.setRepeatMode(Animation.REVERSE);
mAnimationSet.setInterpolator(new LinearInterpolator());
效果如下:
LayoutAnimation
LayoutAnimation用于ViewGroup,为ViewGroup指定一个动画,这样它的所有子View在出场时都带有指定的动画效果。
xml 方式如下:
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animationOrder="normal"
android:animation="@anim/layout_anim"
android:delay="0.5">
</layoutAnimation>
layout_anim 如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:interpolator="@android:anim/linear_interpolator"
android:repeatMode="reverse"
android:shareInterpolator="true">
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:repeatCount="-1"
android:toXDelta="100"
android:toYDelta="0" />
<rotate
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:toDegrees="360" />
</set>
android:delay
表示子元素开始动画的延迟,比如子元素的入场时间周期为500ms,那么0.5 表示每个子元素都要延迟500*0.5=250ms后才会开始播放。总体来说就是第一个子元素延迟250ms播放,第二个子元素延迟500ms播放,后面的子元素以此类推。
android:animationOrder
表示子元素动画的顺序,有三种选项:normal,reverse,random,其中normal表示顺序显示,即排在前面的子元素先开始播放入场动画;reverse表示逆向显示,即排在后面的子元素先开始播放入场动画;random则表示随机播放入场动画。
使用方式有两种:
xml 方式使用,直接在ViewGroup内指定android:layoutAnimation属性。
<zeng.fanda.com.pratice9.view.LayoutAnimationLayout
android:layout_width="match_parent"
android:layoutAnimation="@anim/layout_anim_layout"
android:layout_height="match_parent">
代码方式实现:
mAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.layout_anim);
LayoutAnimationController controller = new LayoutAnimationController(mAnimation);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
controller.setDelay(0.5f);
setLayoutAnimation(controller);
startLayoutAnimation();
效果如下:
View动画属性配置中 %以及%p的含义
android:fromXDelta=”X”,X>0 表示View动画的开始位置是以当前View的原点向右偏移X个位置,同理,X<0时View动画的开始位置是以当前View的原点向左偏移X个位置。
android:fromXDelta=”X%”,X>0 表示View动画的开始位置是以当前View的原点向右偏移View宽度的X%(View.widthX%)个位置,同理,X<0时View动画的开始位置是以当前View的原点向左偏移向右偏移View宽度的X%(View.widthX%)个位置
android:fromXDelta=”X%p”,X>0 表示View动画的开始位置是以当前View的父View的原点向右偏移父View宽度的X%(View.Parent.widthX%)个位置,同理,X<0时View动画的开始位置是以当前View的父View的原点向左偏移父View宽度的X%(View.Parent.widthX%)个位置
View动画事件监听
mAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
// 动画开始时回调
Toast.makeText(getContext(),"onAnimationStart",Toast.LENGTH_SHORT).show();
}
@Override
public void onAnimationEnd(Animation animation) {
// 动画结束时回调
Toast.makeText(getContext(),"onAnimationEnd",Toast.LENGTH_SHORT).show();
}
@Override
public void onAnimationRepeat(Animation animation) {
// 动画循环时,重新下一个循环时回调
Toast.makeText(getContext(),"onAnimationRepeat",Toast.LENGTH_SHORT).show();
}
});
Activity之间的切换动画
有两种实现方式,在Activity中提供了overridePendingTransition(int enterAnim, int exitAnim) 方法,该方法接收两个参数,第一个参数是Activity进入时的动画,第二个参数是Activity退出时的动画。该方法一般写在startActivity()后和finish()后,如果我们想打开或者退出不显示动画,可将参数设置为0。
假设有A、B两界面,当前界面处于A,要打开B界面。
如果是写在startActivity()后,enterAnim 是B界面的进入动画,exitAnim 是A(当前)界面的退出动画。
如果是写在finish()后,enterAnim 是A界面的进入动画,exitAnim 是B(当前)界面的退出动画。
演示的动画没有实际的意义,只是为了显示出界面切换时动画的执行情况。
由上述分析可知,我们需要4个不同的动画文件,以下便是4个动画的 xml 文件:
B界面的进入动画(slide_in_right.xml):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
android:duration="1300"
android:fromXDelta="100%p"
android:toXDelta="0%p" />
</set>
B界面的退出动画(slide_out_right.xml):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1300"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%p"
android:toXDelta="100%p" />
</set>
A界面的进入动画(slide_in_left.xml):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1300"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="-40%p"
android:toXDelta="0%p" />
<alpha
android:fromAlpha="0.5"
android:toAlpha="1" />
</set>
A界面的退出动画(slide_out_left.xml):
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1300"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
android:fromXDelta="0%p"
android:toXDelta="-100%p" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.5" />
</set>
代码实现 :
//打开界面
startActivity(new Intent(MainActivity.this, Pratice9Activity.class));
overridePendingTransition(R.anim.slide_in_right,R.anim.slide_out_left);
//关闭界面
finish();
overridePendingTransition(R.anim.slide_in_left,R.anim.slide_out_right);
不要动画,可以这样写
overridePendingTransition(0,0);
主题实现 :
我们可以在主题上统一我们的界面的切换动画,当你的界面应用上该主题时,动画即生效,无需写任何代码。
xml 文件如下:
<style name="MyCustomActivityTheme" parent="AppTheme">
<item name="android:windowAnimationStyle">@style/AnimationStyle</item>
</style>
<style name="AnimationStyle">
<item name="android:activityOpenEnterAnimation">@anim/slide_in_right</item>
<item name="android:activityOpenExitAnimation">@anim/slide_out_left</item>
<item name="android:activityCloseEnterAnimation">@anim/slide_in_left</item>
<item name="android:activityCloseExitAnimation">@anim/slide_out_right</item>
</style>
通过 windowAnimationStyle 来引用需要的动画,结合上面的分析即可知道 theme 中 activityOpenEnterAnimation 等属性对应的动画意思。
在你要使用切换动画的界面中引用该主题即可生效:
<activity android:name=".MainActivity"
android:theme="@style/MyCustomActivityTheme">
<activity android:name=".ui.Pratice9Activity"
android:theme="@style/MyCustomActivityTheme"/>
效果如下:
注意
View动画的主体是View,更准确的说是View的副本(影子),View动画更改的只是显示,其x,y坐标仍然没有改变,响应事件的位置没有改变,也就是说view本身并没有改变。
也因此,不要使用View动画做交互性操作,例如点击。现在View动画已经很少人使用了,不过View动画简单易用,可以用来做一些简单的不需要交互的动画。
xml动画文件里的数值不能加 “f” ,比如:
<alpha
android:duration="1300"
android:fromAlpha="1.0f"
android:toAlpha="0.85f" />
不能使用 1.0f 和0.85f ,正常的文件属性如下:
<alpha
android:duration="1300"
android:fromAlpha="1.0"
android:toAlpha="0.85" />