本篇文章已授权微信公众号 顾林海 独家发布
Android中的动画分为三种:视图动画、帧动画以及属性动画,视图动画通过对View进行图像变换(平移、缩放、旋转、透明度)从而产生动画效果,帧动画可以通过顺序播放一系列图像从而产生动画,属性动画是API 11加入的,通过动态地改变对象的属性从而达到动画效果,接下来看看这三种动画的具体实现。
帧动画
帧动画比较简单,就是顺序播放一组预先定义好的图片,通过AnimationDrawable来使用,首先在xml文件中定义一个AnimationDrawable,frame_animation.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/p1"
android:duration="500" />
<item
android:drawable="@drawable/p2"
android:duration="500" />
<item
android:drawable="@drawable/p3"
android:duration="500" />
</animation-list>
以animation-list为标签,oneshot属性代表是否循环播放,false为循环播放,true代表只播放一次,内部通过一个个item标签设置每一帧播放的图片以及动画时长,将上面的drawable作为ImageView的背景:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_frame"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="帧动画"/>
<ImageView
android:id="@+id/iv_frame"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_centerInParent="true"
android:background="@drawable/frame_animation"/>
</RelativeLayout>
通过View的getBackground()方法获取AnimationDrawable对象,并通过AnimationDrawable的start()方法启动帧动画,代码如下:
public class MainActivity extends AppCompatActivity {
private Button mFrameBtn;
private ImageView mFrameIv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFrameBtn=findViewById(R.id.btn_frame);
mFrameIv=findViewById(R.id.iv_frame);
mFrameBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AnimationDrawable animationDrawable= (AnimationDrawable) mFrameIv.getBackground();
animationDrawable.start();
}
});
}
}
运行效果如下:
使用帧动画时需要注意每一帧图片的大小不要过大,否则会造成OOM。
视图动画
视图动画支持四种动画效果,分别是平移、缩放、旋转以及透明度。这四种动画既可以通过XML来定义,也可以通过代码来创建。
1、平移
XML定义:
在res/anim/下创建translate.xml:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fillAfter="true"
android:duration="5000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="150"
android:toYDelta="150" />
fillAfter表示动画结束后View是否停留在结束位置,true为停留在结束位置,false为不停留。
duration表示动画的持续时间。
fromXDelta表示x的起始值。
fromYDelta表示y的起始值。
toXDelta表示x的结束值。
toYDelta表示y的结束值。
interpolator表示插值器,插值器会影响动画的速度,常用的插值器如下:
android:interpolator="@android:anim/accelerate_interpolator" 设置动画为加速动画(动画播放中越来越快)
android:interpolator="@android:anim/decelerate_interpolator" 设置动画为减速动画(动画播放中越来越慢)
android:interpolator="@android:anim/accelerate_decelerate_interpolator" 设置动画为先加速在减速(开始速度最快 逐渐减慢)
android:interpolator="@android:anim/anticipate_interpolator" 先反向执行一段,然后再加速反向回来(相当于我们弹簧,先反向压缩一小段,然后在加速弹出)
android:interpolator="@android:anim/anticipate_overshoot_interpolator" 同上先反向一段,然后加速反向回来,执行完毕自带回弹效果(更形象的弹簧效果)
android:interpolator="@android:anim/bounce_interpolator" 执行完毕之后会回弹跳跃几段(相当于我们高空掉下一颗皮球,到地面是会跳动几下)
android:interpolator="@android:anim/cycle_interpolator" 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2* mCycles* Math.PI* input)
android:interpolator="@android:anim/linear_interpolator" 线性均匀改变
android:interpolator="@android:anim/overshoot_interpolator" 加速执行,结束之后回弹
使用方式如下:
mTranslateBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation animation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.translate);
mImageIv.startAnimation(animation);
}
});
通过AnimationUtils的静态方法loadAnimation加载translate文件,再通过View的startAnimation方法传入Animation对象并启动动画。
运行效果如下:
代码实现如下:
final TranslateAnimation translateAnimation=new TranslateAnimation(0,150,0,150);
translateAnimation.setDuration(5000);
translateAnimation.setFillAfter(true);
translateAnimation.setInterpolator(new AccelerateInterpolator());
mTranslateBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mImageIv.startAnimation(translateAnimation);
}
});
运行效果和上面的一样,其中interpolator、fillAfter以及duration也适用于其他三种动画效果,更多属性请查询官方文档。
2、缩放
XML定义:
在res/anim/下创建scale.xml
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fillAfter="true"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromXScale="1"
android:fromYScale="1"
android:toXScale="0.5"
android:toYScale="0.5"
android:pivotX="50%"
android:pivotY="50%"/>
fromXScale表示水平方向缩放的起始值。
fromYScale表示竖直方向缩放的起始值。
toXScale表示水平方向的结束值。
toYScale表示竖直方向的结束值。
pivotX表示缩放的轴点的x坐标,缩放起点x轴坐标,取值可以是数值(50)、百分数(50%)、百分数p(50%p),当取值为数值时,缩放起点为View左上角坐标加具体数值像素,当取值为百分数时,表示在当前View左上角坐加上View宽度的具体百分比,当取值为百分数p时,表示在View左上角坐标加上父控件宽度的具体百分比。
pivotY表示缩放的轴点的y坐标,取值含义与pivotX相同。
使用方式如下:
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation animation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.scale);
mImageIv.startAnimation(animation);
}
});
运行效果:
代码实现如下:
final ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0.5f,
1, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(5000);
scaleAnimation.setFillAfter(true);
scaleAnimation.setInterpolator(new AccelerateInterpolator());
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mImageIv.startAnimation(scaleAnimation);
}
});
3、旋转
XML定义:
在res/anim/下创建rotate.xml:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fillAfter="true"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromDegrees="0"
android:toDegrees="360"
android:repeatCount="-1"
android:pivotX="50%"
android:pivotY="50%"/>
fromDegrees表示旋转的开始角度
toDegrees表示旋转的开始角度
repeatCount表示播放次数,-1表示循环播放
使用方式:
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation rotateAnimation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.rotate);
mImageIv.startAnimation(rotateAnimation);
}
});
运行效果:
代码实现如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = findViewById(R.id.btn_translate);
mImageIv = findViewById(R.id.iv_image);
final RotateAnimation rotateAnimation = new RotateAnimation(0,360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(2000);
rotateAnimation.setFillAfter(true);
rotateAnimation.setInterpolator(new AccelerateInterpolator());
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mImageIv.startAnimation(rotateAnimation);
}
});
}
4、透明度
XML定义:
在res/anim/下创建alpha.xml:
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fillAfter="true"
android:interpolator="@android:anim/accelerate_interpolator"
android:fromAlpha="1"
android:toAlpha="0"
android:pivotX="50%"
android:pivotY="50%"/>
fromAlpha表示透明度的起始值
toAlpha表示透明度的结束值
使用方式:
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation animation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.alpha);
mImageIv.startAnimation(animation);
}
});
运行效果:
代码实现如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = findViewById(R.id.btn_translate);
mImageIv = findViewById(R.id.iv_image);
final AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f,0.0f);
alphaAnimation.setDuration(2000);
alphaAnimation.setFillAfter(true);
alphaAnimation.setInterpolator(new AccelerateInterpolator());
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mImageIv.startAnimation(alphaAnimation);
}
});
}
除了上面单个执行动画外,我们可以同时执行多个,在xml中通过set标签,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator"
android:fillAfter="false"
android:shareInterpolator="true">
<alpha
android:duration="2000"
android:fromAlpha="1"
android:toAlpha="0"
android:pivotX="50%"
android:pivotY="50%"/>
<rotate
android:duration="2000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"/>
<scale
android:duration="2000"
android:fromXScale="1"
android:fromYScale="1"
android:toXScale="0.5"
android:toYScale="0.5"
android:pivotX="50%"
android:pivotY="50%"/>
<translate
android:duration="2000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="150"
android:toYDelta="150" />
</set>
使用方式:
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation animation=AnimationUtils.loadAnimation(MainActivity.this,R.anim.set);
mImageIv.startAnimation(animation);
}
});
运行效果:
代码实现如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = findViewById(R.id.btn_translate);
mImageIv = findViewById(R.id.iv_image);
final AnimationSet animationSet=new AnimationSet(true);
animationSet.setFillAfter(false);
animationSet.setDuration(2000);
AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f,0.0f);
RotateAnimation rotateAnimation = new RotateAnimation(0,360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0.5f,
1, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
TranslateAnimation translateAnimation=new TranslateAnimation(0,150,0,150);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(translateAnimation);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mImageIv.startAnimation(animationSet);
}
});
}
动画播放的相关操作可以通过AnimationListener接口来监听,实现方式如下:
final AnimationSet animationSet=new AnimationSet(true);
animationSet.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
//动画开始
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束
}
@Override
public void onAnimationRepeat(Animation animation) {
//重复播放
}
});
属性动画
属性动画是API 11新加入的,除了能实现上面视图动画的四种动画效果外,还可以持续更加绚丽的效果,同时修复了视图动画带来的不可点击的问题,当View通过视图动画进行一系列的操作后,比如向右偏移100,这时点击这个View时没有任何反应,点击一开始的位置,也就是空白的地方,会触发点击事件,事件不能随动画的变换而变化,新加入的属性动画修复了这些问题。下面主要介绍属性动画中的ValueAnimator、ObjectAnimator以及AnimatorSet等概念。
属性动画中使用ObjectAnimator进行更精细化的控制,控制一个对象和一个属性值,使用多个ObjectAnimator组合到AnimatorSet形成一个动画。属性动画可以通过属性get、set方法来真实地控制一个View的属性值。
ObjectAnimator
ObjectAnimator是属性动画中非常重要的类,创建一个ObjectAnimator只需要通过它的静态工厂类直接返回一个ObjectAnimator对象,参数包括一个对象和对象的属性名字,但这个属性必须有get和set方法,内部会通过反射机制来调用set方法修改对象的属性值。
private void animator(){
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(mBtnNormal,"translationX",100);
objectAnimator.setDuration(3000);
objectAnimator.start();
}
以上代码完成mBtnNormal按钮向右平移100。
可以给ObjectAnimator对象设置时长、插值器等属性。
View常用的属性动画的属性值有以下几种:
translationX和translationY:沿x轴或y轴进行平移。
rotation、rotationX、rotationY:按指定支点进行旋转。
PrivotX和PrivotY:设置支点位置。
alpha:透明度。
x和y:坐标位置。
使用ObjectAnimator时,被操作对象的属性值必须有set和get方法,如果没有,可以定义一个属性类或包装类来间接地给这个属性增加get和set方法。
ObjectAnimator提供了addListener方法:
private void animator(){
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(mBtnNormal,"translationX",100);
objectAnimator.setDuration(3000);
//监听方式一
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
//监听方式二
objectAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
objectAnimator.start();
}
其中Animator.AnimatorListener必须实现它的4个方法,而AnimatorListenerAdapter可以按需实现相关方法。
ValueAnimator
ValueAnimator本身不会实现任何动画效果,可以通过它产生一定规律的数字,通过这些数字控制动画的实现过程,可以使用AnimatorUpdateListener来监听数值的变化。
private void valueAnimator(){
ValueAnimator animator=ValueAnimator.ofFloat(0,1000);
animator.setDuration(3000);
animator.setTarget(mBtnNormal);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value=animation.getAnimatedFraction();
}
});
animator.start();
}
AnimatorSet
AnimatorSet是组合动画,提供了一个play()方法,可以传入Animator对象(ObjectAnimator或ValueAnimator)。play()方法会返回一个AnimatorSet.Builder实例,内部提供了以下4个方法:
after(Animator anim):将现有动画插入到传入的动画之后执行。
after(long delay):将现有动画延迟指定毫秒后执行。
before(Animator anim):将现有动画插入到传入的动画之前执行。
with(Animator anim):将现有动画和传入的动画同时执行。
private void animatorSet(){
ObjectAnimator animator1=ObjectAnimator.ofFloat(mIvImage,"translationX",0,200,0);
ObjectAnimator animator2=ObjectAnimator.ofFloat(mIvImage,"scaleX",1.0f,2.0f,1.0f);
ObjectAnimator animator3=ObjectAnimator.ofFloat(mIvImage,"rotationX",0.0f,360.0f);
AnimatorSet animationSet=new AnimatorSet();
animationSet.setDuration(3000);
animationSet.play(animator1).with(animator2).after(animator3);
animationSet.start();
}
效果如下: