参考:
https://www.jianshu.com/p/87373134481b
https://mp.weixin.qq.com/s/KiZY2HJDoV2HNKrR_2Fp1A
练习github地址:https://github.com/fandazeng/ViewDemo
Frame Animation(帧动画)
Frame Animation其实就是将一系列的图片一张一张的展示出来,有两种实现方式。
1.1 xml 实现方式
它的 xml 语法如下:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="["true" | "false"]>
<item
android:drawable="@[package:]drawable/drawable_resourece_name"
android:duration="integer"/>
</animation-list>
属性比较简单,animation-list 是动画的根元素,在根元素中的oneshot属性表示动画执行次数(默认是false),如果设置为true表示只播放一次,如果false则表示会一直循环执行。在根元素下有item元素,该元素就是我们要添加的图片,每一个item表示一帧,item下的drawable就是我们的图片资源,duration就是该帧动画执行的时间。例如:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@mipmap/run1" android:duration="200"/>
<item android:drawable="@mipmap/run2" android:duration="200"/>
<item android:drawable="@mipmap/run3" android:duration="200"/>
<item android:drawable="@mipmap/run4" android:duration="200"/>
<item android:drawable="@mipmap/run5" android:duration="200"/>
<item android:drawable="@mipmap/run6" android:duration="200"/>
<item android:drawable="@mipmap/run7" android:duration="200"/>
<item android:drawable="@mipmap/run8" android:duration="200"/>
</animation-list>
使用方法如下:
//可以在 xml 属性中设置
mFragmeAnimationIcon.setBackgroundResource(R.drawable.frame_run_animation);
mAnimationDrawable = (AnimationDrawable) mFragmeAnimationIcon.getBackground();
mAnimationDrawable.start();
1.2 代码实现方式
private int[] drawableRes = new int[]{R.mipmap.run1,R.mipmap.run2,R.mipmap.run3,R.mipmap.run4,
R.mipmap.run5,R.mipmap.run6,R.mipmap.run7,R.mipmap.run8};
private void startFrameAnimation() {
mAnimationDrawable = new AnimationDrawable();
for (int resId : drawableRes) {
mAnimationDrawable.addFrame(getResources().getDrawable(resId),200);
}
//设置不是执行一次,即会永久循环播放动画
mAnimationDrawable.setOneShot(false);
mFragmeAnimationIcon.setImageDrawable(mAnimationDrawable);
mAnimationDrawable.start();
}
以上两种方式的实现都需要注意 OOM 问题,其次还有一些其他的方法,比如动画是否进行中,是否要停止动画,例如:
if (mAnimationDrawable != null && mAnimationDrawable.isRunning()) {
mAnimationDrawable.stop();
}
效果如下:
Tween Animation(补间动画)
主要分为四种,分别是位移、缩放、旋转、透明度。它们的语法如下图所示:
<?xml version="1.0" encoding="utf-8"?>
<set xmIns:android="http://schemas. android.com/apk/res/android"
//插值器
android: interpolator="@[package:]anim/interpolator resource'
//动画结束后View是否停留在结束的位置
android: fillAfter=["true" | "false"]
//重复的模式,默认为restart,即重头开始重新运行,reverse即从结束开始向前重新运行
android: repeatMode="restart/reverse"
//子元素是否共用此插值器
android: shareInterpolator=["true" | "false"] >
<alpha
//开始透明度0.0 (全透明)到1.0 (完全不透明)
android: fromAlpha="float"
android: toAlpha="float"
//动画执行时间
android: duration="integer" />
<scale
//1的时候表示不缩放,小于1缩小,大于1放大
android: fromXScale="float"
android: toxScale="float"
android: fromYScale="float"
android: toYScale="float"
//缩放中心,也可以穿fraction值
android: pivotX="float"
android: pivotY="float"
//动画执行时间
android: duration="integer" />
<transLate
//表示X的起始值
android: fromXDelta="float/fraction"
android: toXDelta="float"
android: fromYDelta="float"
android: toYDelta="float"
android:duration=" integer" />
<rotate
//起始的旋转角度
android: fromDegrees="float"
android: toDegrees="float"
android: pivotX="fLoat"
android: pivoty="fLoat"
android: duration=" integer" >
...
</set>
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" />