Android中的动画有三种,分别是补间动画、帧动画、属性动画。
Frame Animation(帧动画)主要用于播放一帧帧准备好的图片,类似GIF图片,优点是使用简单方便、缺点是需要事先准备好每一帧图片;
Tween Animation(补间动画)仅需定义开始与结束的关键帧,而变化的中间帧由系统补上,优点是不用准备每一帧,缺点是只改变了对象绘制,而没有改变View本身属性。因此如果改变了按钮的位置,还是需要点击原来按钮所在位置才有效;
Property Animation(属性动画)是3.0后推出的动画,优点是使用简单、降低实现的复杂度、直接更改对象的属性、几乎可适用于任何对象而仅非View类,缺点是需要3.0以上的API支持,限制较大。
Frame Animation(帧动画)
1、介绍
优点:使用简单、方便
缺点:容易引起 OOM,因为会使用大量 & 尺寸较大的图片资源
2、使用
方式1:XML实现
步骤1、在 res/anim的文件夹里创建动画效果.xml文件
res/drawable/anim_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<!--图片格式必须是png-->
<item
android:drawable="@drawable/l1"
android:duration="300"/>
<item
android:drawable="@drawable/l2"
android:duration="300"/>
<item
android:drawable="@drawable/l3"
android:duration="300"/>
<item
android:drawable="@drawable/l4"
android:duration="300"/>
<item
android:drawable="@drawable/l5"
android:duration="300"/>
<item
android:drawable="@drawable/l6"
android:duration="300"/>
<item
android:drawable="@drawable/l7"
android:duration="300"/>
</animation-list>
步骤2、设置动画资源(图片资源)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start"/>
<Button
android:id="@+id/stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="stop"/>
<ImageView
android:id="@+id/mIvImg"
android:layout_marginTop="50dp"
android:layout_gravity="center_horizontal"
android:layout_width="200dp"
android:layout_height="200dp"/>
</LinearLayout>
步骤3、在Java代码中载入 & 启动动画
public class FrameAnimationActivity extends Activity{
@BindView(R.id.start)
Button start;
@BindView(R.id.stop)
Button stop;
@BindView(R.id.mIvImg)
ImageView mIvImg;
private AnimationDrawable animationDrawable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animationdrawable);
ButterKnife.bind(this);
mIvImg.setBackgroundResource(R.drawable.anim_drawable);
animationDrawable = (AnimationDrawable)mIvImg.getBackground();
}
@OnClick({R.id.start,R.id.stop})
public void onClick(View v) {
switch (v.getId()){
case R.id.start:
start();
break;
case R.id.stop:
stop();
break;
default:
break;
}
}
private void start(){
animationDrawable.start();
}
private void stop(){
animationDrawable.stop();
}
}
方式2:在Java代码中实现
步骤1、设置动画资源
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="start"/>
<Button
android:id="@+id/stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="stop"/>
<ImageView
android:id="@+id/mIvImg"
android:layout_marginTop="50dp"
android:layout_gravity="center_horizontal"
android:layout_width="200dp"
android:layout_height="200dp"/>
</LinearLayout>
步骤2、java代码动态加载
public class FrameAnimationActivity1 extends Activity{
@BindView(R.id.start)
Button start;
@BindView(R.id.stop)
Button stop;
@BindView(R.id.mIvImg)
ImageView mIvImg;
private AnimationDrawable animationDrawable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animationdrawable1);
ButterKnife.bind(this);
}
@OnClick({R.id.start,R.id.stop})
public void onClick(View v) {
switch (v.getId()){
case R.id.start:
start();
break;
case R.id.stop:
stop();
break;
default:
break;
}
}
private void start(){
//创建帧动画
animationDrawable = new AnimationDrawable();
//添加帧
animationDrawable.addFrame(getResources().getDrawable(R.drawable.l1), 300);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.l2), 300);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.l3), 300);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.l4), 300);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.l5), 300);
//设置动画是否只播放一次, 默认是false
animationDrawable.setOneShot(false);
//根据索引获取到那一帧的时长
int duration = animationDrawable.getDuration(2);
//根据索引获取到那一帧的图片
Drawable drawable = animationDrawable.getFrame(0);
//判断是否是在播放动画
boolean isRunning = animationDrawable.isRunning();
//获取这个动画是否只播放一次
boolean isOneShot = animationDrawable.isOneShot();
//获取到这个动画一共播放多少帧
int framesCount = animationDrawable.getNumberOfFrames();
//把这个动画设置为background,兼容更多版本写下面那句
mIvImg.setBackground(animationDrawable);
mIvImg.setBackgroundDrawable(animationDrawable);
animationDrawable.start();
}
private void stop(){
animationDrawable.stop();
}
}
Tween Animation(补间动画)
View动画是一种渐进式动画,定义动画开始和结束的两帧,并指定动画变化的时间和方式。并通过平移、缩放、旋转和透明度四种效果结合成复杂的动画效果。
而在开始和结束帧之间插入的渐变值依据的是插值器。
1、 应用场景
(1)、标准的动画效果
补间动画常用于视图View的一些标准动画效果:平移、旋转、缩放 & 透明度;
除了常规的动画使用,补间动画还有一些特殊的应用场景。
(2)、 特殊的应用场景
Activity 的切换效果
Fragement 的切换效果
视图组(ViewGroup)中子元素的出场效果
2、 应用
步骤1、创建动画效果
res/anim/rotate_demo.xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360">
</rotate>
步骤2、创建资源布局
res/layout/activity_tweenanimation.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/mcreateRotateAnimation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="旋转"/>
<Button
android:id="@+id/mcreateScaleAnimation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="放大缩小"/>
<Button
android:id="@+id/mcreateTranslateAnimation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="平移"/>
<Button
android:id="@+id/mcreateAlphaAnimation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="透明"/>
<Button
android:id="@+id/mcreateSetAnimation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="组合"/>
<ImageView
android:id="@+id/mTv"
android:layout_width="200dp"
android:layout_height="200dp"
android:src="@drawable/l2"/>
</LinearLayout>
步骤3、java代码触发动画
/**
* 创建旋转动画
*/
private void createRotateAnimation() {
// 步骤2:创建 动画对象 并传入设置的动画效果xml文件
Animation ratateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate_demo);
// 步骤3:播放动画
mTv.startAnimation(ratateAnimation);
// 步骤4:监听动画
ratateAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
Log.e(tag, "1=================Start");
}
@Override
public void onAnimationRepeat(Animation animation) {
Log.e(tag, "1=================Repeat");
}
@Override
public void onAnimationEnd(Animation animation) {
Log.e(tag, "1=================End");
}
});
}
Property Animation(属性动画)
数值发生器 -- ValueAnimator类
定义:属性动画机制中 最核心的一个类
实现动画的原理:通过不断控制 值 的变化,再不断 手动 赋给对象的属性,从而实现动画效果。
从上面原理可以看出:ValueAnimator类中有3个重要方法:
ValueAnimator.ofInt(int values)
ValueAnimator.ofFloat(float values)
ValueAnimator.ofObject(int values)
1、ValueAnimator.ofInt(int values)
作用:将初始值 以整型数值的形式 过渡到结束值
即估值器是整型估值器 - IntEvaluator
估值器 -- TypeEvaluator
插值器(Interpolator)决定 值 的变化模式(匀速、加速blabla)
估值器 -- TypeEvaluator
估值器(TypeEvaluator)决定 值 的具体变化数值
举例-- ValueAnimator.ofInt
private void doAnimator() {
final int top = m1.getTop();
final int left = m1.getLeft();
final int width = m1.getMeasuredWidth();
final int height = m1.getMeasuredHeight();
ValueAnimator animator = ValueAnimator.ofInt(0, 400,0,300,0,200,0,100,0,50,0,40,0,30,0,20,0,10,0,5,0);
// 设置动画运行的时长
animator.setDuration(4000);
//多久后开始动画
animator.setStartDelay(0);
//循环次数
animator.setRepeatCount(5);
//无穷循环
// animator.setRepeatCount(ValueAnimator.INFINITE);
//repeat的时候正向重新计算
animator.setRepeatMode(ValueAnimator.RESTART);
//repeat的时候反向重新计算
// animator.setRepeatMode(ValueAnimator.REVERSE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// 获得改变后的值
int value = (int) valueAnimator.getAnimatedValue();
// 将改变后的值赋给对象的属性值
m1.layout(left,top-value,left+width,top-value+height);
}
});
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
Log.e(tag,"onAnimation Start");
}
@Override
public void onAnimationEnd(Animator animator) {
Log.e(tag,"onAnimation End");
}
@Override
public void onAnimationCancel(Animator animator) {
Log.e(tag,"onAnimation Cancel");
}
@Override
public void onAnimationRepeat(Animator animator) {
Log.e(tag,"onAnimation Repeat");
}
});
animator.start();
}
举例-- 插值器和估值器
/**
* 抛物线
* @param view
*/
public void paowuxian(View view)
{
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(3000);
valueAnimator.setObjectValues(new PointF(0, 0));
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator<PointF>()
{
// fraction = t / duration
@Override
public PointF evaluate(float fraction, PointF startValue,
PointF endValue)
{
Log.e(tag, fraction * 3 + "");
// x方向200px/s ,则y方向0.5 * 10 * t
PointF point = new PointF();
point.x = 200 * fraction * 3;
point.y = 0.5f * 200 * (fraction * 3) * (fraction * 3);
return point;
}
});
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
PointF point = (PointF) animation.getAnimatedValue();
m1.setX(point.x);
m1.setY(point.y);
}
});
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
Log.e(tag,"onAnimation Start");
}
@Override
public void onAnimationEnd(Animator animator) {
Log.e(tag,"onAnimation End");
}
@Override
public void onAnimationCancel(Animator animator) {
Log.e(tag,"onAnimation Cancel");
}
@Override
public void onAnimationRepeat(Animator animator) {
Log.e(tag,"onAnimation Repeat");
}
});
}
源码:
https://github.com/yuanhhyuan/UI4
参考文章:
Android 逐帧动画:关于 逐帧动画 的使用都在这里了!
https://www.jianshu.com/p/225fe1feba60
Android 动画:手把手教你使用 补间动画
https://www.jianshu.com/p/733532041f46
Property Animation(属性动画)
https://www.jianshu.com/p/2412d00a0ce4