一、内容概述
本篇所涉及的内容如下
补间动画
- ScaleAnimation 缩放动画
- AlphaAnimation 透明度动画
- TranslateAnimation 位移动画
- RotateAnimation 旋转动画
- AnimationSet View动画集合
- 自定义View动画
帧动画 (FrameAnimation 或称 Drawable Animation)
布局动画
- LayoutAnimation 基于View动画实现
- LayoutTransition 基于属性动画实现
二、补间动画
原理:通过起点和终点的位置来模拟出中间位置所形成的动画效果
特点:不改变view的自身属性,移动的只是一份影像
优劣:适宜做复杂的动画效果,但不适宜与有用户交互的场合
1.ScaleAnimation 缩放动画
示例
public void scale(View view) {
Button scaleButton = (Button) view;
// ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0.5f, 1,
0.5f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
// 解析xml文件获取ScaleAnimation对象
ScaleAnimation scaleAnimation = (ScaleAnimation) AnimationUtils.loadAnimation(this, R.anim.animation_scale);
// 设置动画时长
scaleAnimation.setDuration(1000);
// 保持动画后的位置
scaleAnimation.setFillAfter(true);
// 设置Interpolator
scaleAnimation.setInterpolator(new AccelerateInterpolator());
//设置重复次数
scaleAnimation.setRepeatCount(Animation.INFINITE);
// 设置重复模式
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleButton.startAnimation(scaleAnimation);
}
解析ScaleAnimation
常用构造
- public ScaleAnimation(float fromX, float toX, float fromY, float toY)
- public ScaleAnimation(float fromX, float toX, float fromY, float toY, float pivotX, float pivotY) //可设置缩放中心
- public ScaleAnimation(float fromX, float toX, float fromY, float toY,int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) //可设置缩放中心(pivotValue)与相对缩放对象(pivotType)
两种相对缩放 (后续补间动画均有此项,不赘述)
pivotType:
- Animation.RELATIVE_TO_SELF //相对自己
- RELATIVE_TO_PARENT // 相对父容器
中心点 pivotX,pivotY 三种种写法(后续补间动画均有此项,不赘述)
- android:pivotX="50%", 相对自身
- android:pivotX=="%50p" 相对父容器
- android:pivotX="200" 指定位置
常用插值器
@android:anim/overshoot_interpolator
@android:anim/accelerate_decelerate_interpolator
@android:anim/accelerate_interpolator
@android:anim/decelerate_interpolator
@android:anim/anticipate_interpolator
@android:anim/anticipate_overshoot_interpolator
@android:anim/bounce_interpolator
@android:anim/linear_interpolator
@android:anim/cycle_interpolator
常见选项 (后续补间动画均有此项,不赘述)
- setInterpolator // 设置插值器
- setRepeatCount // 设置重复次数,Animation.INFINITE(-1)表示无限循环
- setRepeatMode // 设置重复模式 ,Animation.REVERSE反转、Animation.RESTART重新开始
- setDuration //设置持续时长(ms)
- setFillAfter // 设置是否保持动画后的位置
- setStartOffset // 设置开始动画时间延迟
- setAnimationListener 设置动画监听
- 等。。
补间动画监听
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
// 动画开始时
@Override
public void onAnimationStart(Animation animation) {
}
// 动画结束时
@Override
public void onAnimationEnd(Animation animation) {
}
// 动画结束时
@Override
public void onAnimationRepeat(Animation animation) {
}
});
xml文件中的写法(位于anim文件夹)
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXScale="1"
android:fromYScale="1"
android:interpolator="@android:anim/overshoot_interpolator"
android:pivotX="50%"
android:pivotY="50%p"
android:toXScale="0"
android:toYScale="0">
</scale>
2.AlphaAnimation 透明度动画
示例
public void alpha(View view) {
Button alphaButton = (Button) view;
// AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
// 使用AnimationUtils.loadAnimation加载xml写的动画
AlphaAnimation alphaAnimation = (AlphaAnimation) AnimationUtils.loadAnimation(this, R.anim.animation_alpha);
// alphaAnimation.setFillAfter(true);
alphaAnimation.setDuration(1000);
alphaAnimation.setRepeatCount(Animation.INFINITE);
alphaAnimation.setRepeatMode(Animation.REVERSE);
alphaButton.startAnimation(alphaAnimation);`
}
常见构造
public AlphaAnimation(float fromAlpha, float toAlpha)
public AlphaAnimation(Context context, AttributeSet attrs)
xml文件中的写法(位于anim文件夹)
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1"
android:toAlpha="0" />
3.TranslateAnimation 位移动画
示例
public void translation(View view) {
Button translationButton = (Button) view;
// TranslateAnimation translateAnimation = new TranslateAnimation(0, 100, 0, 0);
TranslateAnimation translateAnimation = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.animation_translate);
translateAnimation.setDuration(1000);
translationButton.startAnimation(translateAnimation);
}
常见构造
public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
int fromYType, float fromYValue, int toYType, float toYValue) // 设置平移相对谁平移
xml文件中的写法(位于anim文件夹)
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:interpolator="@anim/cycle6"
android:toXDelta="15"></translate>
4.RotateAnimation 旋转动画
示例
public void rotate(View view) {
Button rotateButton = (Button) view;
RotateAnimation rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(this, R.anim.animation_rotate);
// RotateAnimation rotateAnimation = new RotateAnimation(0,180, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
rotateAnimation.setDuration(1000);
rotateAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
rotateAnimation.setRepeatMode(Animation.REVERSE);
// rotateAnimation.setRepeatCount(Animation.INFINITE); // -1 无限次
rotateButton.startAnimation(rotateAnimation);
}
常见构造
- public RotateAnimation(float fromDegrees, float toDegrees)
- public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) // 指定旋转中心点
- public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,int pivotYType, float pivotYValue) // 设置旋转中心点的值与类型
xml文件中的写法(位于anim文件夹)
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:interpolator="@android:anim/overshoot_interpolator"
android:pivotX="200"
android:pivotY="200"
android:repeatCount="5"
android:toDegrees="360">
</rotate>
5.AnimationSet View动画集合
一个可以容纳四种补间动画的集合动画,通过addAnimation将动画添加至集合动画
示例
public void set(View view) {
Button setButton = (Button) view;
// AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(this, R.anim.animation_set);
// set.setRepeatCount(2);
// set.setRepeatMode(Animation.REVERSE);
// setButton.startAnimation(set);
AlphaAnimation aa = new AlphaAnimation(1, 0);
ScaleAnimation sa = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
RotateAnimation ra = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
AnimationSet set = new AnimationSet(true);
set.setDuration(1000);
// set.setStartOffset(1500); // 开始动画延迟
set.setRepeatCount(3);
set.setRepeatMode(Animation.RESTART);
set.addAnimation(aa);
set.addAnimation(sa);
set.addAnimation(ra);
setButton.startAnimation(set);
}
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/overshoot_interpolator"
android:repeatCount="1"
android:repeatMode="restart"
android:shareInterpolator="true" >
<rotate
android:fromDegrees="0"
android:interpolator="@android:anim/overshoot_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360"></rotate>
<alpha
android:fromAlpha="0.5"
android:toAlpha="1"></alpha>
<scale
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%p"
android:pivotY="50%"
android:toXScale="0"
android:toYScale="0" />
</set>
xml文件中相关参数解析
- android:shareInterpolator 是否共享插值器
- pivotX,pivotY 中心点,参数写法见ScaleAnimation解析
注意事项
- 通过对AnimationSet的使用,可以发现setRepeatCount与setRepeatMode失效
- 动画默认只能一起播放,没有提供按次序执行的方法,提供如下两种参考解决办法
- 设置一个view动画监听,当前一个动画结束时,执行下一个动画
- AnimationSet中设置动画播放延迟,实现次序效果
下面提供第二种解决方案
public void sequence(View view) {
AnimationSet set = new AnimationSet(false);
AlphaAnimation aa = new AlphaAnimation(1, 0.5f);
aa.setDuration(1000);
ScaleAnimation sa = new ScaleAnimation(1, 0.5f, 1, 0.5f);
sa.setDuration(1000);
sa.setStartOffset(2000); // 设置延时
set.addAnimation(aa);
set.addAnimation(sa);
view.startAnimation(set);
}
6.自定义View动画
自定义View动画,首先要继承Animation类,其次实现initialize(int width, int height, int parentWidth, int parentHeight)和applyTransformation(float interpolatedTime, Transformation t)方法。initialize方法用于做初始化工作,诸如获取被animated对象的宽高,此方法在applyTransformation方法之前调用。applyTransformation方法用于操作对象,进行相关的矩阵变换。
简单示例
实现一个左右晃动的振动效果
package com.yu.customviewanimation;
import android.view.animation.Animation;
import android.view.animation.Transformation;
/**
* Created by pecu on 2016/08/25.
*/
public class CustomViewAnimation extends Animation {
int width,height;
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
this.width=width;
this.height=height;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
t.getMatrix().setTranslate((float) (Math.sin(interpolatedTime*4*3.14)*width/5),0); // 实现一个左右晃动的振动效果
}
}
三、帧动画(FrameAnimation)
使用步骤
在drawable目录下创建frame_anim.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/p5" android:duration="300" />
<item android:drawable="@mipmap/p4" android:duration="300" />
<item android:drawable="@mipmap/p3" android:duration="300" />
<item android:drawable="@mipmap/p2" android:duration="300" />
<item android:drawable="@mipmap/p1" android:duration="300" />
</animation-list>
在java代码中
ImageView imageView = (ImageView) findViewById(R.id.lv_frame);
AnimationDrawable drawable = (AnimationDrawable) imageView.getDrawable();
drawable.start();
注意事项
由于帧动画是利用多张图片堆叠出来的效果,加上安卓对每个应用的内存限制,容易造成内存溢出(OOM),因此在开发中慎用帧动画。使用帧动画时,尽量使用尺寸较小的图片
四、布局动画
1、LayoutAnimation
主要用于ViewGroup中的子view的出场动画
使用步骤
在xml中定义LayoutAnimation动画
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/anim_translate"
android:animationOrder="normal"
android:delay="0.2"
android:interpolator="@android:anim/overshoot_interpolator"></layoutAnimation>
关于参数android:delay= fraction,表示item相对于上一个item出场的时间延迟,为0~1之间的float值,即为item出场时间的比例=duration*fraction
具体条目动画anim_translate.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromXDelta="-100%"
android:interpolator="@android:anim/overshoot_interpolator"
android:toXDelta="0">
</translate>
注意:anim_translate中给设置item动画的anim文件设置druation,否则无效果
在xml中给ListView设置布局动画,通过android:layoutAnimation指定
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutAnimation="@anim/anim_layout" />
亦可java代码中可以直接加载xml中定义的布局动画,然后设置给ListView
listView = (ListView) findViewById(R.id.lv);
TranslateAnimation ta = (TranslateAnimation) AnimationUtils.loadAnimation(this, R.anim.anim_translate);
LayoutAnimationController lac = new LayoutAnimationController(ta);
lac.setDelay(0.5f);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(lac);
2、LayoutTransition
用于ViewGroup中的子view的变化对自己,对ViewGroup的影响所需的动画,即有View添加、删除、隐藏、显示的时候才会体现出来。
几种转换动画类型:
1.LayoutTransition.APPEARING:当View出现或者添加的时候View出现的动画。
2.LayoutTransition.CHANGE_APPEARING:当添加View导致布局容器改变的时候整个布局容器的动画。
3.LayoutTransition.DISAPPEARING:当View消失或者隐藏的时候View消失的动画。
4.LayoutTransition.CHANGE_DISAPPEARING:当删除或者隐藏View导致布局容器改变的时候整个布局容器的动画。
5.LayoutTransition.CHANGE:当不是由于View出现或消失造成对其他View位置造成改变的时候整个布局容器的动画。
使用系统提供的默认动画
只需在ViewGroup中添加一句android:animateLayoutChanges=”true”即可
<GridLayout
android:id="@+id/gl"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:animateLayoutChanges="true"
android:columnCount="4"></GridLayout>
使用自定义布局动画
步骤:
1、初始化LayoutTransition对象
LayoutTransition lt = new LayoutTransition();
2、通过setAnimator方法给特定的转换状态指定动画
final ObjectAnimator appearAnim = ObjectAnimator.ofFloat(null, "rotationY", 90, 0);
appearAnim.setDuration(lt.getDuration(LayoutTransition.APPEARING));
lt.setAnimator(LayoutTransition.APPEARING, appearAnim);
3、给ViewGroup设置布局容器动画
gridLayout.setLayoutTransition(lt);
示例
private void setTransition() {
PropertyValuesHolder pvhLeft =
PropertyValuesHolder.ofInt("left", 0, 1);
PropertyValuesHolder pvhTop =
PropertyValuesHolder.ofInt("top", 0, 1);
PropertyValuesHolder pvhRight =
PropertyValuesHolder.ofInt("right", 0, 1);
PropertyValuesHolder pvhBottom =
PropertyValuesHolder.ofInt("bottom", 0, 1);
layoutTransition = new LayoutTransition();
gridLayout.setLayoutTransition(layoutTransition);
/**
* 添加一个Button的出现动画
*/
final ObjectAnimator appearAnim = ObjectAnimator.ofFloat(null, "rotationY", 90, 0);
appearAnim.setDuration(layoutTransition.getDuration(LayoutTransition.APPEARING));
layoutTransition.setAnimator(LayoutTransition.APPEARING, appearAnim);
/**
* 删除一个Button时的消失动画
*/
final ObjectAnimator disappearingAnim = ObjectAnimator.ofFloat(null, "scaleX", 1, 0);
disappearingAnim.setDuration(layoutTransition.getDuration(LayoutTransition.DISAPPEARING));
layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, disappearingAnim);
/**
* LayoutTransition.CHANGE_DISAPPEARING
* 当删除一个Button时,设置其它Button的动画效果
* Keyframe 对象中包含了一个时间/属性值的键值对,用于定义某个时刻的动画状态。
*/
Keyframe mKeyframeStart = Keyframe.ofFloat(0.0f, 360f);
Keyframe mKeyframeMiddle = Keyframe.ofFloat(0.5f, 180f);
Keyframe mKeyframeEnd = Keyframe.ofFloat(1.0f, 0f);
PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation",
mKeyframeStart, mKeyframeMiddle, mKeyframeEnd);
ObjectAnimator changeDisAppearingAnim = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft, pvhTop, pvhRight, pvhBottom, mPropertyValuesHolder).setDuration(layoutTransition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
layoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeDisAppearingAnim);
changeDisAppearingAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
View view = (View) ((ObjectAnimator) animation).getTarget();
view.setRotation(0.0f);
}
});
/**
* LayoutTransition.CHANGE_APPEARING
* 当增加一个Button时,设置其他Button的动画效果 缩放效果
*/
PropertyValuesHolder mHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f, 1.0f);
PropertyValuesHolder mHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.0f, 1.0f);
ObjectAnimator changeAppearingAnim = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,
pvhTop, pvhRight, pvhBottom, mHolderScaleX, mHolderScaleY).setDuration(layoutTransition
.getDuration(LayoutTransition.CHANGE_APPEARING));
layoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAppearingAnim);
changeAppearingAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
View view = (View) ((ObjectAnimator) animation).getTarget();
view.setScaleX(1f);
view.setScaleY(1f);
}
});
}