前言
动画在应用中是非常常见的界面效果,同时也是提高用户体验的一种好手段。可是动画种类繁多,使用复杂,每当需要自定义动画实现复杂的动画效果时,自己总是显得束手无策。接下来就好好屡屡动画到底是怎样的。
一、动画的类型
视图动画:逐帧动画和补间动画 作用对象是:视图
属性动画:Android3.0之后的新特性
(1)补间动画:同一个图形通过视图在界面上进行透明度、缩放、平移、旋转的变化
缩放:ScaleAnimation 放大、缩小视图的大小
透明度:AlphaAnimation 改变视图的透明度
旋转:RotateAnimation 旋转视图的透明度
平移:TranslateAnimation 移动视图的位置
作用对象:视图控件,如TextView、Button,不可作用于view的属性如颜色、背景等
原理:通过确定开始的视图样式&结束的样式、中间动画变化过程由系统补全来确定一个动画
特点:使用简单、方便 缺点:仅控制整体实体效果,无法控制属性
应用场景:Activity、fragment的切换效果,视图中标准、基础的动画效果
具体使用:
Android提供了两种实现动画的方式
①xml配置的方式:动画描述的可读性更好
②纯编码的方式:动画效果可动态创建
设置方法:在xml代码中设置,res/anim/translate_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
// 以下参数是4种动画效果的公共属性,即都有的属性
android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset ="1000" // 动画延迟开始时间(ms)
android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度,下面会详细讲
// 以下参数是平移动画特有的属性
android:fromXDelta="0" // 视图在水平方向x 移动的起始值
android:toXDelta="500" // 视图在水平方向x 移动的结束值
android:fromYDelta="0" // 视图在竖直方向y 移动的起始值
android:toYDelta="500" // 视图在竖直方向y 移动的结束值
android:fromXDelta="0"
android:toXDelta="500"
android:fromYDelta="0"
android:toYDelta="500"
android:duration="1000">
</translate>
在Activity中引用xml动画,比如个ImageView设置动画:
public class AnimationActivity extends AppCompatActivity {
private ImageView mIvImageView;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation);
mIvImageView = findViewById(R.id.iv_image);
btn = findViewById(R.id.btn);
}
//button的点击事件
public void startAnimation(View view){
Animation animation = AnimationUtils.loadAnimation(this, R.anim.translate_animation);
mIvImageView.startAnimation(animation);
}
}
用Java代码实现同样的效果
public class AnimationActivity extends AppCompatActivity {
private ImageView mIvImageView;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation);
mIvImageView = findViewById(R.id.iv_image);
btn = findViewById(R.id.btn);
}
public void startAnimation(View view){
Animation translateAnimation=new TranslateAnimation(0,500,0,500);
// 1. fromXDelta :视图在水平方向x 移动的起始值
// 2. toXDelta :视图在水平方向x 移动的结束值
// 3. fromYDelta :视图在竖直方向y 移动的起始值
// 4. toYDelta:视图在竖直方向y 移动的结束值
translateAnimation.setDuration(1000);
mIvImageView.startAnimation(translateAnimation);
}
}
其他3种动画的设置方式也是一样的,就不一一举例了。但是看下其他动画的一些属性的意思
rotate旋转动画的一些属性
// 以下参数是旋转动画特有的属性
android:duration="1000"
android:fromDegrees="0" // 动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
android:toDegrees="270" // 动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
android:pivotX="50%" // 旋转轴点的x坐标
android:pivotY="0" // 旋转轴点的y坐标
// 轴点 = 视图缩放的中心点
// pivotX pivotY,可取值为数字,百分比,或者百分比p
// 设置为数字时(如50),轴点为View的左上角的原点在x方向和y方向加上50px的点。在Java代码里面设置这个参数的对应参数是Animation.ABSOLUTE。
// 设置为百分比时(如50%),轴点为View的左上角的原点在x方向加上自身宽度50%和y方向自身高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_SELF。
// 设置为百分比p时(如50%p),轴点为View的左上角的原点在x方向加上父控件宽度50%和y方向父控件高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_PARENT
// 两个50%表示动画从自身中间开始,具体如下图
public void startAnimation(View view){
// 参数说明:
// 1. fromDegrees :动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
// 2. toDegrees :动画结束时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
// 3. pivotXType:旋转轴点的x坐标的模式
// 4. pivotXValue:旋转轴点x坐标的相对值
// 5. pivotYType:旋转轴点的y坐标的模式
// 6. pivotYValue:旋转轴点y坐标的相对值
// pivotXType = Animation.ABSOLUTE:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
// pivotXType = Animation.RELATIVE_TO_SELF:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
// pivotXType = Animation.RELATIVE_TO_PARENT:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
Animation rotateAnimation=new RotateAnimation(0,270,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
rotateAnimation.setDuration(2000);
mIvImageView.startAnimation(rotateAnimation);
}
scale缩放动画:
// 以下参数是缩放动画特有的属性
android:fromXScale="0.0"
// 动画在水平方向X的起始缩放倍数
// 0.0表示收缩到没有;1.0表示正常无伸缩
// 值小于1.0表示收缩;值大于1.0表示放大
android:toXScale="2" //动画在水平方向X的结束缩放倍数
android:fromYScale="0.0" //动画开始前在竖直方向Y的起始缩放倍数
android:toYScale="2" //动画在竖直方向Y的结束缩放倍数
android:pivotX="50%" // 缩放轴点的x坐标
android:pivotY="50%" // 缩放轴点的y坐标
// 轴点 = 视图缩放的中心点
// pivotX pivotY,可取值为数字,百分比,或者百分比p
// 设置为数字时(如50),轴点为View的左上角的原点在x方向和y方向加上50px的点。在Java代码里面设置这个参数的对应参数是Animation.ABSOLUTE。
// 设置为百分比时(如50%),轴点为View的左上角的原点在x方向加上自身宽度50%和y方向自身高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_SELF。
// 设置为百分比p时(如50%p),轴点为View的左上角的原点在x方向加上父控件宽度50%和y方向父控件高度50%的点。在Java代码里面设置这个参数的对应参数是Animation.RELATIVE_TO_PARENT
public void startAnimation(View view){
// 参数说明:
// 1. fromX :动画在水平方向X的结束缩放倍数
// 2. toX :动画在水平方向X的结束缩放倍数
// 3. fromY :动画开始前在竖直方向Y的起始缩放倍数
// 4. toY:动画在竖直方向Y的结束缩放倍数
// 5. pivotXType:缩放轴点的x坐标的模式
// 6. pivotXValue:缩放轴点x坐标的相对值
// 7. pivotYType:缩放轴点的y坐标的模式
// 8. pivotYValue:缩放轴点y坐标的相对值
// pivotXType = Animation.ABSOLUTE:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
// pivotXType = Animation.RELATIVE_TO_SELF:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
// pivotXType = Animation.RELATIVE_TO_PARENT:缩放轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)
Animation scaleAnimation=new ScaleAnimation(0,3,0,3,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);
scaleAnimation.setDuration(3000);
mIvImageView.startAnimation(scaleAnimation);
}
透明度Alpha动画
// 以下参数是透明度动画特有的属性
android:fromAlpha="1.0" // 动画开始时视图的透明度(取值范围: -1 ~ 1)
android:toAlpha="0.0"// 动画结束时视图的透明度(取值范围: -1 ~ 1)
public void startAnimation(View view){
// 参数说明:
// 1. fromAlpha:动画开始时视图的透明度(取值范围: -1 ~ 1)
// 2. toAlpha:动画结束时视图的透明度(取值范围: -1 ~ 1) 1:代表完全显示,0时就是完全消失
Animation alphaAnimation=new AlphaAnimation(1,0);
alphaAnimation.setDuration(5000);
mIvImageView.startAnimation(alphaAnimation);
}
(2)逐帧动画:在界面的同一个位置上不断切换显示不同的图片
作用对象:视图控件,如TextView、Button,不可作用于view的属性如颜色、背景等
原理:将动画拆分成帧的形式,且定义每一帧=每一张图片,按顺序播放预先准备好的图片
特点:简单、方便,缺点:容易引起OOM,因为会用大量尺寸较大的图片资源
应用场景:较为复杂的个性化动画效果
举例:帧动画需要在drawable目录下创建xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@mipmap/loading_000" android:duration="100"/>
<item android:drawable="@mipmap/loading_002" android:duration="100"/>
<item android:drawable="@mipmap/loading_004" android:duration="100"/>
<item android:drawable="@mipmap/loading_006" android:duration="100"/>
<item android:drawable="@mipmap/loading_008" android:duration="100"/>
<item android:drawable="@mipmap/loading_010" android:duration="100"/>
<item android:drawable="@mipmap/loading_012" android:duration="100"/>
<item android:drawable="@mipmap/loading_014" android:duration="100"/>
<item android:drawable="@mipmap/loading_016" android:duration="100"/>
<item android:drawable="@mipmap/loading_018" android:duration="100"/>
<item android:drawable="@mipmap/loading_020" android:duration="100"/>
<item android:drawable="@mipmap/loading_022" android:duration="100"/>
<item android:drawable="@mipmap/loading_024" android:duration="100"/>
<item android:drawable="@mipmap/loading_026" android:duration="100"/>
<item android:drawable="@mipmap/loading_028" android:duration="100"/>
<item android:drawable="@mipmap/loading_030" android:duration="100"/>
<item android:drawable="@mipmap/loading_032" android:duration="100"/>
<item android:drawable="@mipmap/loading_034" android:duration="100"/>
<item android:drawable="@mipmap/loading_036" android:duration="100"/>
<item android:drawable="@mipmap/loading_040" android:duration="100"/>
<item android:drawable="@mipmap/loading_042" android:duration="100"/>
<item android:drawable="@mipmap/loading_044" android:duration="100"/>
<item android:drawable="@mipmap/loading_046" android:duration="100"/>
<item android:drawable="@mipmap/loading_048" android:duration="100"/>
<item android:drawable="@mipmap/loading_050" android:duration="100"/>
</animation-list>
在Activity中引用
public class AnimationActivity extends AppCompatActivity {
private ImageView mIvImageView;
private Button mBtnStartAnimation;
private Button mBtnStopAnimation;
private AnimationDrawable mAnimationDrable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation);
mIvImageView = findViewById(R.id.iv_image);
mBtnStartAnimation = findViewById(R.id.btn);
mBtnStopAnimation = findViewById(R.id.stop_animation);
}
public void startAnimation(View view) {
mIvImageView.setImageResource(R.drawable.frame_animation);
//开始动画
mAnimationDrable= (AnimationDrawable) mIvImageView.getDrawable();
mAnimationDrable.start();
}
public void stopAnimation(View view) {
mIvImageView.setImageResource(R.drawable.frame_animation);
//开始动画
mAnimationDrable= (AnimationDrawable) mIvImageView.getDrawable();
mAnimationDrable.stop();
}
}
方式二:使用Java代码
public class AnimationActivity extends AppCompatActivity {
private ImageView mIvImageView;
private Button mBtnStartAnimation;
private Button mBtnStopAnimation;
private AnimationDrawable mAnimationDrable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation);
mIvImageView = findViewById(R.id.iv_image);
mBtnStartAnimation = findViewById(R.id.btn);
mBtnStopAnimation = findViewById(R.id.stop_animation);
mAnimationDrable = new AnimationDrawable();
for (int i = 0; i <= 25; i++) {
//如果是放在mipmap下的,将第二个参数换成是mipmap就可以了
int id = getResources().getIdentifier("a" + i, "drawable", getPackageName());
Drawable drawable = getResources().getDrawable(id);
mAnimationDrable.addFrame(drawable, 100);
}
}
public void startAnimation(View view) {
mAnimationDrable.setOneShot(true);
mIvImageView.setImageDrawable(mAnimationDrable);
mAnimationDrable.stop();
// 特别注意:在动画start()之前要先stop(),不然在第一次动画之后会停在最后一帧,这样动画就只会触发一次
mAnimationDrable.start();
}
public void stopAnimation(View view) {
//开始动画
mAnimationDrable.setOneShot(true);
mIvImageView.setImageDrawable(mAnimationDrable);
mAnimationDrable.stop();
}
}
特别注意图片命名的格式:顺序要挨着,最好不要出现下划线之类的,命名的越简单获取的时候的越简单
好了至此补间动画和逐帧动画就演示完了!
三、复合动画(AnimationSet)
复合动画就是多个单一动画组合在一起。
AnimatorSet.play(Animator anim) :播放当前动画
AnimatorSet.after(long delay) :将现有动画延迟x毫秒后执行
AnimatorSet.with(Animator anim) :将现有动画和传入的动画同时执行
AnimatorSet.after(Animator anim) :将现有动画插入到传入的动画之后执行
AnimatorSet.before(Animator anim) : 将现有动画插入到传入的动画之前执行
xml配置文件实现组合动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="2000"
android:fromAlpha="0"
android:toAlpha="1"></alpha>
<rotate
android:duration="2000"
android:fromDegrees="0"
android:toDegrees="360"></rotate>
<!--"Xml复合动画: 透明度从透明到不透明, 持续2s, 接着进行旋转360度的动画, 持续2s"-->
</set>
在Activity中引用:
package com.example.wcystart.otherproject;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
public class AnimationActivity extends AppCompatActivity {
private ImageView mIvImageView;
private Button mBtnStartAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation);
mIvImageView = findViewById(R.id.iv_image);
mBtnStartAnimation = findViewById(R.id.btn);
}
public void startAnimation(View view) {
Animation animationSet = AnimationUtils.loadAnimation(this,R.anim.combine_animation);
animationSet.setDuration(2000);
mIvImageView.startAnimation(animationSet);
}
}
使用Java代码实现复合动画:
public class AnimationActivity extends AppCompatActivity {
private ImageView mIvImageView;
private Button mBtnStartAnimation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation);
mIvImageView = findViewById(R.id.iv_image);
mBtnStartAnimation = findViewById(R.id.btn);
}
public void startAnimation(View view) {
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
alphaAnimation.setDuration(1000);
RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(1000);
//合并到复合动画中
AnimationSet animationSet = new AnimationSet(false);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(rotateAnimation);
mIvImageView.startAnimation(animationSet);
}
}
四、动画监听器
Animation.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animation animation) {
//动画开始时执行
}
@Override
public void onAnimationRepeat(Animation animation) {
//动画重复时执行
}
@Override
public void onAnimationCancel()(Animation animation) {
//动画取消时执行
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束时执行
}
});
因Animator类包括(ValueAnimator类(又包括ObjectAnimator类)和AnimatorSet类),所以他们几个都可设置addListener
使用动画适配器可实现监听4个方法中的某一个,
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
}
});
四、属性动画(重点学习)
学习链接:Android属性动画