Android动画(帧动画补间动画属性动画)讲解

Posted Coder.追

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android动画(帧动画补间动画属性动画)讲解相关的知识,希望对你有一定的参考价值。

android动画(帧动画、补间动画、属性动画)讲解

首先我们来看看啥是帧动画、补间动画、属性动画。

介绍:

帧动画:是一种常见的动画形式(Frame By Frame),其原理是在“连续的关键帧”中分解动画动作,也就是在时间轴的每帧上逐帧绘制不同的内容,使其连续播放而成动画。

补间动画:指的是做FLASH动画时,在两个关键帧中间需要做“补间动画”,才能实现图画的运动;

属性动画:帧动画与补间动画实现了对View进行移动、缩放、旋转和淡入淡出的效果。但对于android开发师来说是不够的,同时移动、缩放、旋转和淡入淡出的效果也不再只是一种视觉上的动画效果了。所以从Android 3.0版本开始,系统给我们提供了一种全新的动画模式,属性动画(property animation)。

所以今天我们来看看,这些动画是怎样实现的。

帧动画(我们先来看看效果)

首先我们创建我们要的素材,把他们导入到drawable文件夹下,如图。

然后在drawable文件新建from.xml文件,写入代码,这里的作用可以理解为,我们把图片
放到一个集合里面去了,要用的时候我们直接调用from。

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/a" android:duration="120" />
    <item android:drawable="@drawable/b" android:duration="120" />
    <item android:drawable="@drawable/c" android:duration="120" />
    <item android:drawable="@drawable/d" android:duration="120" />
    <item android:drawable="@drawable/e" android:duration="120" />
    <item android:drawable="@drawable/f" android:duration="120" />
    <item android:drawable="@drawable/g" android:duration="120" />
    <item android:drawable="@drawable/h" android:duration="120" />

</animation-list>

然后在activity_main.xml文件里写入

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tl"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/from">

</RelativeLayout>

然后在MainActivity.java文件写入代码

package com.example.a2022324;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.widget.RelativeLayout;

public class MainActivity extends AppCompatActivity 

    private boolean flag;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RelativeLayout relativelayout = findViewById(R.id.tl); //获取activity——main的RelativeLayout 的id
        AnimationDrawable anim = (AnimationDrawable) relativelayout.getBackground();
        // anim作用获取背景,这里我们的被禁就是我们设置的图片集合(from)
        relativelayout.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                if(flag)//用开关来实现点击动与不动的效果
                    anim.start();
                    flag = false;
                else
                    anim.stop();
                    flag = true;
                
            
        );
    


我们点击运行完成了效果。

补间动画

alpha:图片渐渐地显示,渐渐地消失的效果。
rotat:图片的以某一点为中心,旋转xx度
scale:图片的放大与缩小
translate:图片的平移

然后我们来看看补间动画,首先来看看效果。

我们在res文件先创建amin目录在分别创建alpha、rotate、scale、tranlslate.xml文件。

先看看alpha.xml和activity.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" />

</set>
<?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"
    tools:ignore="MissingClass">

    <ImageView
        android:id="@+id/iv"
        android:src="@drawable/background"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"//说明一下,第一个属性,只要你容器是RelativeLayout的时候才有。此时设置为RelativeLayout里的子控件属性为android:layout_centerInParent=”true“,就是水平垂直都居中。
        android:adjustViewBounds="true"//调整ImageView的边界,使得ImageView和图片有一样的长宽比例。
        android:maxHeight="300dp"
        android:maxWidth="300dp"/>

</RelativeLayout>

android:adjustViewBounds的用法这里可以看看这位小哥哥的我是连接(https://www.jianshu.com/p/49f8d5e5965b?utm_campaign=haruki)

再看看MainActivity.java文件,这里我把其他功能也加了上去,避免代码冗余,不过注释掉了,不会影响结果。

package com.example.a2022324;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ImageView imageview = findViewById(R.id.iv);//获取id值

        imageview.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) //通过animation来加载alpha文件,下面的原理同理
                Animation animation = AnimationUtils.loadAnimation(MainActivity.this,
                        R.anim.alpha);

//                Animation animation = AnimationUtils.loadAnimation(MainActivity.this,
//                        R.anim.rotate);

//                Animation animation = AnimationUtils.loadAnimation(MainActivity.this,
//                        R.anim.scale);

//                Animation animation = AnimationUtils.loadAnimation(MainActivity.this,
//                        R.anim.translate);
                imageview.startAnimation(animation);
            
        );

    



属性动画

我们先来看看效果。

这里我们可以看出,文字渐隐出现,我们直接看代码

activity_main

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:ignore="MissingClass">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="快看我,我要变化了"
        android:textSize="20sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

这里我们用到了androidx.constraintlayout.widget.ConstraintLayout这一坨玩意,
如果你的包红了,那是因为没用引入库。在app->的build.gradle文件的dependencies写入==implementation ‘androidx.constraintlayout:constraintlayout:1.1.3’==这一坨玩意。如图。

我们再来看看,mainactivity代码:

package com.example.a2022324;

import androidx.appcompat.app.AppCompatActivity;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);//改变float的值,0f是透明的,1f是不透明的
        // ValueAnimator.ofArgb()
        valueAnimator.setDuration(2000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() 

            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) 
                float value = (float) valueAnimator.getAnimatedValue();
            
        );
        valueAnimator.start();

        TextView textView = findViewById(R.id.tv);
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(textView, "alpha",
                0f, 1f);//objectAnimator 作用是  让我们的textView的透明度从0f到1f
        objectAnimator.setDuration(4000);//从0f到1f要4秒
        objectAnimator.start();//启动

    


ValueAnimator的用法可以看看这位大佬的用法我是连接
(https://blog.csdn.net/u011043551/article/details/65938908)

效果完成。(求关注)

Android动画-帧动画补间动画属性动画

帧动画(Frame Animation)

定义: 帧动画就是按照顺序播放一帧一帧的照片达到动画的效果。

我们可以看一下实现过程:在drawable目录下新建frame_list.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/frame_animation_1"
        android:duration="300" />
    <item
        android:drawable="@drawable/frame_animation_2"
        android:duration="300" />
    <item
        android:drawable="@drawable/frame_animation_3"
        android:duration="300" />
</animation-list>

具体代码中使用如下:

@BindView(R.id.image_container)
    ImageView imageContainer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_animation);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.frame_animation_start)
    public void onStartFrameAnimation() {
        imageContainer.setImageResource(R.drawable.frame_list);
        AnimationDrawable animationDrawable = (AnimationDrawable)imageContainer.getDrawable();
        animationDrawable.start();
    }

    @OnClick(R.id.frame_animation_end)
    public void onEndFrameAnimation() {
        AnimationDrawable animationDrawable = (AnimationDrawable)imageContainer.getDrawable();
        animationDrawable.stop();
    }
  1. Activity中使用imageContainer作为播放的容器
  2. (AnimationDrawable)imageContainer.getDrawable() 能获取到帧动画的对象进行播放/停止操作

备注:
帧动画使用简单方便,但是注意在比较复杂的时候图片比较多的时候容易发生oom,因此尽量避免使用尺寸较大的图片。

补间动画(Tween Animation)

定义:与前面写了帧动画不同,帧动画是通过连续播放图片来模拟动画效果,而补间动画开发者只需指定动画开始,以及动画结束"关键帧", 而动画变化的"中间帧"则由系统计算并补齐!它只是去改变了视觉效果,并不会改变控件本身的属性。补间动画包括基本的平移动画,缩放动画,旋转动画和透明度动画,我们也可以将这些基本动画进行组合在一起。

android支持的补间动画类型

AlphaAnimation:透明度渐变效果,创建时许指定开始以及结束透明度,还有动画的持续 时间,透明度的变化范围(0,1),0是完全透明,1是完全不透明;对应<alpha/>标签!
ScaleAnimation:缩放渐变效果,创建时需指定开始以及结束的缩放比,以及缩放参考点, 还有动画的持续时间;对应<scale/>标签!
TranslateAnimation:位移渐变效果,创建时指定起始以及结束位置,并指定动画的持续 时间即可;对应<translate/>标签!
RotateAnimation:旋转渐变效果,创建时指定动画起始以及结束的旋转角度,以及动画 持续时间和旋转的轴心;对应<rotate/>标签
AnimationSet:组合渐变,就是前面多种渐变的组合,对应<set/>标签

每种动画都可以通过两种方式去实现,代码实现和xml文件实现,个人肯定倾向于代码去做。

xml文件实现

xml文件如下:

translate xmlns:android="http://schemas.android.com/apk/res/android"
   android:duration="3000"
   android:startOffset ="1000"
   android:fillAfter = "false"
   android:repeatMode= "reverse"
   android:repeatCount = "infinite"
   android:interpolator="@android:anim/accelerate_decelerate_interpolator"

   android:fromXDelta="0"
   android:toXDelta="500"
   android:fromYDelta="0"
   android:toYDelta="0">

   <!--duration:动画时长
   startOffset:动画延迟开始时间
   fillAfter:动画结束后是否停留在结束状态
   repeatMode:重复播放动画模式restart代表正序重放,reverse代表倒序回放
   repeatCount:重放次数(+1),为infinite时无限重复
   interpolator:插值器:选择动画变化的模式,有匀速,加速,减速,先加速在减速。。。

   以上是所有补间动画共有的属性,以下是平移动画特有的属性
   fromXDelta:平移动画在水平方向x初始值
   toXDelta:平移动画在水平方向x结束值
   fromYDelta:平移动画在竖直方向y初始值
   toYDelta:平移动画在竖直方向y结束值-->
</translate>

Activity中实现如下:

    @OnClick(R.id.transfer_animation_xml)
    public void onTransferXML() {
        Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_translate);
        mImageContainer.startAnimation(animation);
    }

代码实现

通过代码写一个补件动画也是很简单的:

    @OnClick(R.id.transfer_animation_java)
    public void onTransferJava() {
        Animation transferAnimation = new TranslateAnimation(0, 500, 0, 0);
        transferAnimation.setRepeatMode(Animation.RESTART);
        transferAnimation.setDuration(3000);
        transferAnimation.setRepeatCount(3);
        mImageContainer.startAnimation(transferAnimation);
    }

动画结束后 组件的位置:
不改变动画的属性,所以最后位置也不会改变。
是否可以组件:
不同的补间动画可以重叠在一起:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:duration="1000"
        android:fromAlpha="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:toAlpha="1" />
    <translate
        android:duration="2000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:startOffset="3000"
        android:toXDelta="500"
        android:toYDelta="0" />
    <rotate
        android:duration="3000"
        android:fromDegrees="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:repeatCount="infinite"
        android:repeatMode="restart"
        android:toDegrees="360" />
    <scale
        android:duration="3000"
        android:fromXScale="1"
        android:fromYScale="1"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="4000"
        android:toXScale="0.5"
        android:toYScale="0.5" />
</set>

备注: 以上所有的动画都可以设置listener监听其执行过程

属性动画(Property Animator)

定义:通过控制view的属性来实现动画
 1、为什么引入 Property Animator(属性动画)    
我提出一个假设:请问大家,如何利用补间动画来将一个控件的背景色在一分钟内从绿色变为红色?这个效果想必没办法仅仅通过改变控件的渐入渐出、移动、旋转和缩放来实现吧,而这个效果是可以通过 Property Animator 完美实现的 这就是第一个原因:Property Animator 能实现补间动画无法实现的功能 大家都知道,补间动画和逐帧动画统称为 View Animation,也就是说这两个动画只能对派生自 View 的控件实例起作用;而 Property Animator 则不同,从名字中可以看出属性动画,应该是作用于控件属性的!正因为属性动画能够只针对控件的某一个属性来做动画,所以也就造就了他能单独改变控件的某一个属性的值!比如颜色!这就是 Property Animator 能实现补间动画无法实现的功能的最重要原因。 我们得到了第二点不同:View Animation 仅能对指定的控件做动画,而 Property Animator 是通过改变控件某一属性值来做动画的。 假设我们将一个按钮从左上角利用补间动画将其移动到右下角,在移动过程中和移动后,这个按钮都是不会响应点击事件的。这是为什么呢?因为补间动画仅仅转变的是控件的显示位置而已,并没有改变控件本身的值。View Animation 的动画实现是通过其 Parent View 实现的,在 View 被 drawn 时 Parents View 改变它的绘制参数,这样虽然 View 的大小或旋转角度等改变了,但 View 的实际属性没变,所以有效区域还是应用动画之前的区域;我们看到的效果仅仅是系统作用在按钮上的显示效果,利用动画把按钮从原来的位置移到了右下角,但按钮内部的任何值是没有变化的,所以按钮所捕捉的点击区域仍是原来的点击区域。(下面会举例来说明这个问题) 这就得到了第三点不同:补间动画虽能对控件做动画,但并没有改变控件内部的属性值。而 Property Animator 则是恰恰相反,Property Animator 是通过改变控件内部的属性值来达到动画效果的

ValueAnimator

简单的实现,实际场景中可以在listener中去更新view的属性。 下面的代码是通过改变属性让view向右下方移动400像素

@OnClick(R.id.value_animation)
    public void onValueAnimation() {
        int left = mImageContainer.getLeft();
        int right = mImageContainer.getRight();
        int top = mImageContainer.getTop();
        int bottom = mImageContainer.getBottom();
        Log.d(TAG, "onValueAnimation: left=" + mImageContainer.getLeft() + "    right=" + mImageContainer.getRight()
                + " top=" + mImageContainer.getTop() + "    bottom=" + mImageContainer.getBottom());
        ValueAnimator animator = ValueAnimator.ofInt(0,400);
        animator.setDuration(4000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (Integer) animation.getAnimatedValue();
                Log.d(TAG, "onAnimationUpdate: " + value);
                mImageContainer.layout(left + value, top +value, right + value, bottom + value);
            }
        });
        animator.start();
    }

ObjectAnimator

ObjectAnimator 是对ValueAnimator的封装与扩展,上面的例子开发者还需要关心view的update的过程,ObjectAnimator则是将该过程封装起来对开发者不可见。
 ValueAnimator 有个缺点,就是只能对数值对动画计算。我们要想对哪个控件操作,需要监听动画过程,在监听中对控件操作。这样使用起来相比补间动画而言就相对比较麻烦。 为了能让动画直接与对应控件相关联,以使我们从监听动画过程中解放出来,谷歌的开发人员在 ValueAnimator 的基础上,又派生了一个类 ObjectAnimator; 由于 ObjectAnimator 是派生自 ValueAnimator 的,所以 ValueAnimator 中所能使用的方法,在 ObjectAnimator 中都可以正常使用。 但 ObjectAnimator 也重写了几个方法,比如 ofInt(),ofFloat()等。我们先看看利用 ObjectAnimator 重写的 ofFloat 方法如何实现一个动画:(改变透明度)

改变透明度的属性动画

    @OnClick(R.id.object_animation_simple)
    public void onObjectAnimationSimple() {
        ObjectAnimator animator = ObjectAnimator.ofFloat(mPointView,"alpha",1,0,1);
        animator.setDuration(2000);
        animator.start();
    }

通过set函数改变自定义view属性

public class MyPointView extends View {

    private int mRadius;

    public MyPointView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(300,300, mRadius, paint);
        super.onDraw(canvas);
    }

    public int getPointRadius(){
        return 50;
    }

    public void setPointRadius(int radius){
        mRadius = radius;
        invalidate();
    }
}

        // activity中测试代码
    @OnClick(R.id.object_animation_selfview)
    public void onObjectAnimationSelfView() {
        ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius",100);
        animator.setDuration(2000);
        animator.start();
    }

性能对比

代码地址: https://github.com/lsfzlj/AndroidTestNeil/tree/develop

参考:
ValueAnimator 基本使用
Android动画之帧动画,补间动画和属性动画

梦想不是浮躁,而是沉淀和积累

以上是关于Android动画(帧动画补间动画属性动画)讲解的主要内容,如果未能解决你的问题,请参考以下文章

Android动画总结(属性动画,补间动画,帧动画)

Android动画-帧动画补间动画属性动画

Android 动画 属性动画 视图动画 补间动画 帧动画 详解 使用

Android中补间动画和属性动画

Android 基础的三种动画 帧动画补间动画属性动画。

Android 动画使用及源码分析