Android的三种动画详解(帧动画,View动画,属性动画)

Android的三种动画详解(帧动画、View动画、属性动画)_android动画效果大全-CSDN博客

1、帧动画

缺点是:占用内存较高,播放的是一帧一帧的图片,很少使用

顺序播放预先定义的图片,类似于播放视频。

步骤:

1).在drawable文件夹下创建一个animation_picture.xml文件,Root element选择为animation-list.

具体为:右键点击drawable文件夹->New→Drawable Resource File

2)配置自己需要播放的图片

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <?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/food1" android:duration=``"500"``/> ``<item android:drawable=``"@drawable/food2" android:duration=``"500"``/> ``<item android:drawable=``"@drawable/food3" android:duration=``"500"``/> ``<item android:drawable=``"@drawable/laojunshan1" android:duration=``"500"``/> ``<item android:drawable=``"@drawable/laojunshan2" android:duration=``"500"``/> <``/animation-list``> |

上述xml中,有些属性我们要了解到:

  • 1、android:oneshot="false": 表示是否重复播放动画,还是只播放一次;
  • 2、每个item都有Drawable和duration属性,Drawable表示我们要播放的图片;duration表示这张图播放的时间;
3).将animation_picture.xml设置为imageview的播放资源

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| package com.example.animationtest; import androidx.appcompat.app.AppCompatActivity; import android.graphics.drawable.AnimationDrawable; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; public class MainActivity ``extends AppCompatActivity { ``private ImageView mImageView; ``private AnimationDrawable animationDrawable = ``null``; ``private boolean flag = ``true``; ``@Override ``protected void onCreate(Bundle savedInstanceState) { ``super``.onCreate(savedInstanceState); ``setContentView(R.layout.activity_main); ``Button button = (Button) findViewById(R.id.animationButton); ``mImageView = (ImageView)findViewById(R.id.image_view); ``mImageView.setBackgroundResource(R.drawable.animation_picture);``//设置资源文件 ``animationDrawable = (AnimationDrawable) mImageView.getBackground(); ``button.setOnClickListener(``new View.OnClickListener() { ``@Override ``public void onClick(View v) { ``if (flag) ``{ ``animationDrawable.start();``//开启动画 ``flag = ``false``; ``}``else``{ ``animationDrawable.stop(); ``flag = ``true``; ``} ``} ``}); ``} } |

实际上,从名字也可以看出,AnimationDrawable是一个Drawable的子类,所以我们定义的xml文件也是放在res/rawable目录下的.

2、View动画

View动画是补间动画,设定起始和终止位置,中间会自动补齐,有平移、缩放、旋转、透明四种选择。对应的类为TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation。

view动画也称为补间动画,因为我们只需要拿到一个view,设定它开始和结束的位置,中间的view会自动由系统补齐,而不需要帧动画每一幅图都是提前准备好的。

View动画是Android一开始就提供的比较原始的动画,主要支持四种效果:平移、缩放、旋转、透明度变化(渐变) 四种基本效果,我们可以再这四种基础效果的基础上,选择其中的几种进行组合。

优点:效率高,使用方便。

缺点:交互性差,当动画结束后会回到初始位置,对于交互性要求较高的使用属性动画。

(1) 作用对象局限于View

(2) 动画效果单一,仅能实现位移、旋转、缩放、透明度四种属性的改变

(3) 没有改变View真实属性

可以使用xml配置资源文件实现,也可以用代码实现,这里用代码实现。其中包含按钮控制动画和默认显示组合动画。

Bar.java

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| package com.example.viewanimationtest; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.annotation.SuppressLint; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.RotateAnimation; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; /* *Since the child thread cannot directly modify the main UI, *it is implemented using the Handler mechanism. * */ public class MainActivity ``extends AppCompatActivity ``implements View.OnClickListener{ ``private ImageView mImageView; ``public static final int TRANSLATE_ANI = ``1``; ``public static final int ROTATE_ANI = ``2``; ``public static final int SCALE_ANI = ``3``; ``public static final int ALPHA_ANI = ``4``; ``private Handler mHandler = ``new Handler(Looper.getMainLooper()){ ``@Override ``public void handleMessage(``@NonNull Message msg) { ``switch``(msg.what) ``{ ``case TRANSLATE_ANI: ``mImageView.clearAnimation();``//When setting a new animation, first clear the previous animation. ``Animation translateAnimation = ``new TranslateAnimation(``0``,``500``, ``0``,``500``);``//The animation moves from(0,0)to (500,500). ``translateAnimation.setDuration(``2000``); ``mImageView.setAnimation(translateAnimation); ``break``; ``case ROTATE_ANI: ``mImageView.clearAnimation(); ``Animation rotateAnimation = ``new RotateAnimation(``0``,``360``, ``0``,``0``);``//The animation rotates from 0° to 360° around(0,0). ``rotateAnimation.setDuration(``2000``); ``mImageView.setAnimation(rotateAnimation); ``break``; ``case SCALE_ANI: ``mImageView.clearAnimation(); ``Animation scaleAnimation = ``new ScaleAnimation(``0``,``1``,``0``,``1``); ``scaleAnimation.setDuration(``2000``); ``mImageView.setAnimation(scaleAnimation); ``break``; ``case ALPHA_ANI: ``mImageView.clearAnimation(); ``Animation alphaAnimation = ``new AlphaAnimation(``0``,``1``); ``alphaAnimation.setDuration(``2000``); ``mImageView.setAnimation(alphaAnimation); ``break``; ``default``: ``Log.d(``"1111"``,``"default"``); ``break``; ``} ``} ``}; ``@Override ``protected void onCreate(Bundle savedInstanceState) { ``super``.onCreate(savedInstanceState); ``setContentView(R.layout.activity_main); ``mImageView =(ImageView)findViewById(R.id.image_view); ``Button translateButton = (Button) findViewById(R.id.translate_button); ``Button scaleButton = (Button) findViewById(R.id.scale_button); ``Button rotateButton = (Button) findViewById(R.id.rotate_button); ``Button alphaButton = (Button) findViewById(R.id.alpha_button); ``translateButton.setOnClickListener(``this``); ``scaleButton.setOnClickListener(``this``); ``rotateButton.setOnClickListener(``this``); ``alphaButton.setOnClickListener(``this``); ``mImageView.clearAnimation();``//If some animations exist, clear them first. ``//set mix animation ``AnimationSet animationSet = ``new AnimationSet(``true``); ``//set default alpha animation ``Animation alphaAnimation = ``new AlphaAnimation(``0``,``1``);``//The animation is from fully transparent to fully opaque. ``alphaAnimation.setDuration(``2000``);``//The animation lasts 2 seconds. ``//set default scale animation ``Animation scaleAnimation = ``new ScaleAnimation(``0``,``1``,``0``,``1``);``//The animation is from 0% to 100%. ``scaleAnimation.setDuration(``2000``); ``//add sub-animations to combined animation ``animationSet.addAnimation(alphaAnimation); ``animationSet.addAnimation(scaleAnimation); ``//show the combined animation ``mImageView.setAnimation(animationSet); ``} ``@SuppressLint``(``"NonConstantResourceId"``) ``@Override ``public void onClick(View v) { ``switch (v.getId()) ``{ ``case R.id.translate_button: ``new Thread(``new Runnable() { ``@Override ``public void run() { ``Message msg = ``new Message(); ``msg.what = TRANSLATE_ANI; ``mHandler.sendMessage(msg); ``} ``}).start(); ``break``; ``case R.id.rotate_button: ``new Thread(``new Runnable() { ``@Override ``public void run() { ``Message msg = ``new Message(); ``msg.what = ROTATE_ANI; ``mHandler.sendMessage(msg); ``} ``}).start(); ``break``; ``case R.id.scale_button: ``new Thread(``new Runnable() { ``@Override ``public void run() { ``Message msg = ``new Message(); ``msg.what = SCALE_ANI; ``mHandler.sendMessage(msg); ``} ``}).start(); ``break``; ``case R.id.alpha_button: ``new Thread(``new Runnable() { ``@Override ``public void run() { ``Message msg = ``new Message(); ``msg.what = ALPHA_ANI; ``mHandler.sendMessage(msg); ``} ``}).start(); ``break``; ``default``: ``break``; ``} ``} } |

具体效果

​编辑view.mp4

3.属性动画

跟补间动画类似。具体内容可以参考文章:

Android的三种动画详解(帧动画、View动画、属性动画)

在Android3.0以后引入了这种动画模式,用来弥补传统的补间动画和帧动画的不足。属性动画的核心类如下图所示:

其中Animator是属性动画的基类,提供了一些通用的方法。AnimatorSet相当于一组属性动画的容器,用来同时执行多个属性动画。app开发中常用的属性动画为ValueAnimator和ObjectAnimator,本文将基于Android O的版本详细介绍ValueAnimator在系统中的实现方式。

优点:交互性强,动画结束时的位置就是最终位置。详细使用可参考:Android进阶之光 书籍

代码实现

Bar.java

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| package com.example.propertyanimation; import androidx.appcompat.app.AppCompatActivity; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.os.Bundle; import android.view.animation.AnimationSet; import android.widget.ImageView; import java.util.Set; public class MainActivity ``extends AppCompatActivity { ``private ImageView mImageview; ``@Override ``protected void onCreate(Bundle savedInstanceState) { ``super``.onCreate(savedInstanceState); ``setContentView(R.layout.activity_main); ``mImageview = (ImageView) findViewById(R.id.image_view); ``//use AnimatorSet to show the combined animation ``AnimatorSet animatorSet = ``new AnimatorSet(); ``//Use the ofFloat function to construct an ObjectAnimator object and set the alpha parameter. ``ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageview, ``"alpha"``,0f,``1``.0f); ``//Set the translationY parameter to move to a certain position along the Y axis ``ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageview, ``"translationY"``, ``200``); ``//Set the translationX parameter to move to a certain position along the X axis ``ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageview, ``"translationX"``, ``200``); ``//Set the rotation parameter to rotate a certain angle ``ObjectAnimator animator4 = ObjectAnimator.ofFloat(mImageview, ``"rotation"``, ``180``); ``//Magnify 0.5 times ``ObjectAnimator animator5 = ObjectAnimator.ofFloat(mImageview, ``"scaleX"``, ``0``.5f); ``//The animation lasts 2 seconds. ``animatorSet.setDuration(``2000``); ``//set the combined animation ``animatorSet.playTogether( ``animator1, ``animator2, ``animator3, ``animator4, ``animator5 ``); ``//start the animation ``animatorSet.start(); ``} } |

1.使用方法

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| private void testValueAnimator() { ``ValueAnimator valueAnimator = ValueAnimator.ofInt(``0``, ``100``); ``valueAnimator.setDuration(``300``); ``valueAnimator.addListener(``new Animator.AnimatorListener() { ``@Override ``public void onAnimationStart(Animator animation) { ``Log.d(TAG, ``"onAnimationStart"``); ``} ``@Override ``public void onAnimationEnd(Animator animation) { ``Log.d(TAG, ``"onAnimationEnd"``); ``} ``@Override ``public void onAnimationCancel(Animator animation) { ``} ``@Override ``public void onAnimationRepeat(Animator animation) { ``} ``}); ``valueAnimator.addUpdateListener(``new ValueAnimator.AnimatorUpdateListener() { ``@Override ``public void onAnimationUpdate(ValueAnimator animation) { ``Log.d(TAG, ``"fraction: " + animation.getAnimatedFraction() + ``" value: " ``+ animation.getAnimatedValue()); ``} ``}); ``Log.d(TAG, ``"testValueAnimator"``); ``valueAnimator.setStartDelay(``100``); ``valueAnimator.start(); } |

其主要分为以下几个过程:

(1) 创建ValueAnimator对象 => 通过ofXXX的静态方法获取

(2) 初始化参数 => 根据一系列setXXX/addXXX方法添加一些初始化参数和回调函数

(3) 开始动画 => 调用start方法

在使用属性动画时,客户端通过ValueAnimator.AnimatorUpdateListener接口的回调函数onAnimationUpdate来监听每一帧的变化。属性动画顾名思义,在动画过程中每一帧都会对预设的属性进行改变,客户端通过每一帧该属性的变化自定义一些操作。其预设的属性就是在获取ValueAnimator对象时,通过ofXXX方法设置的。上例中的ValueAnimator.ofInt(0, 100)则表示:在该属性动画执行的过程中,一个属性名为空字符串(默认属性名,后面会说到属性动画的属性名何时为默认,何时为开发者自定义),属性类型为整型的属性在0 ~ 100区间内递增的变化。在每一帧的回调中,开发者可以通过ValueAnimator#getAnimatedValue方法获取默认属性值在该帧的值为多少。上例中的另一个方法:ValueAnimator#getAnimatedFraction则表示该帧的动画进度(0 ~ 1浮点数)为多少。

2.动画初始化过程

(1) ofXXX方法:设置动画属性类型及变化范围

初始化提供的方法有:ofInt,ofFloat,ofArgb(描述颜色的ARGB值的变化),ofPropertyValuesHolder(自定义的一系列PropertyValuesHolder,使用该方法时,会在动画每一帧分别处理添加进来的PropertyValuesHolder的变化),ofObject(使用该方法时需要自定义类型估值器)。ofInt,ofFloat,ofObject,ofArgb的实质都是先包装出一个对应的PropertyValuesHolder对象,然后当动画驱动时,根据这个对象计算数当前动画的进度(fraction)以及对应类型值在这一帧的取值(animated value)。ofPropertyValuesHolder方法相当于开发者自己创建若干PropertyValuesHolder对象,在动画驱动时批处理这些对象。

如果使用ofPropertyValuesHolder,需要开发者自己创建PropertyValuesHolder对象,需要传入对应改变的属性的名字。如果使用其它初始化方法,则属性名字默认为空字符串("")。客户端通过ValueAnimator#getAnimatedValue方法获取每一帧的属性值为多少:如果调用无参的该方法,则返回的是默认的属性值在当前帧的值,因为默认的初始化方法确定了其属性类型,并且只有一个。如果调用的是带String类型参数的该方法,则返回的是对应名字的属性值在当前帧的值,其名字是在创建PropertyValuesHolder对象时设置的。另外,开发者也可以通过ValueAnimator#setValues方法主动添加PropertyValuesHolder对象进来。

综上,无论通过哪种ofXXX方法进行初始化,最终都会创建一个或多个PropertyValuesHolder对象,这个类是用来管理属性动画中开发者定义的"属性"的变化,每当动画进行时,ValueAnimator对象会计算所有持有的PropertyValuesHolder对象在此帧的属性值为多少,开发者可以通过getAnimatedValue获取。属性动画的核心也在于此------动画驱动过程中,开发者可以自定义任意类型的对象的变化,因此属性动画也打破了补间动画的局限性。

(2) addUpdateListener:添加一个AnimatorUpdateListener的监听者

这个监听者的作用在于:在动画驱动过程中,每一帧都会通过该回调通知给客户端进程,客户端可以在该回调中处理每一帧要做的事情。

(3) addListener:添加一个AnimatorListener的监听者

这个监听者用来监听动画的开始/结束/取消/重复四个行为的发生。

(4) setDuration:添加动画执行时长

(5) setCurrentFraction/setCurrentPlayTime

设置动画开始的动画进度/开始的时间点(0 ~ Duration范围内)

3.动画启动过程:ValueAnimator#start()

动画启动过程的关键流程:(step表示上图中的步骤序号)

step4:向Choreographer中注册一个动画回调,用来驱动整个动画流程。

step6 ~ step7:初始化PropertyValuesHolder对象中的类型估值器,默认只支持Int/Float两种类型的类型估值器,其他类型需要自定义。

step8:通知客户端注册的监听者动画开始:AnimatorListener#onAnimationStart被回调。

step9 ~ step10:设置动画开始时间以及开始时动画进度,如果客户端没有主动通过setCurrentFraction/setCurrentPlayTime设置,则默认会调用setCurrentPlayTime(0),表示动画从头开始。

step13 ~ step15:入口为animateValue,每一帧的动画都会调用到此方法,该方法主要完成以下几件事:

(1) 根据时间进度以及动画插值器计算出当前动画进度(fraction)

(2) 根据当前动画进度以及类型估值器计算出当前PropertyValuesHolder在此帧中的取值是多少(animated value)

(3) 回调AnimatorUpdateListener#onAnimationUpdate方法

启动时会调用该方法,认为start方法触发的行为为第一帧(Choreographer驱动的为第二帧)

4.动画驱动过程:Choreographer#doFrame

相关推荐
帅得不敢出门2 小时前
Android Framework预装traceroute执行文件到system/bin下
android
xzkyd outpaper2 小时前
从面试角度回答Android中ContentProvider启动原理
android·面试·计算机八股
编程乐学2 小时前
基于Android 开发完成的购物商城App--前后端分离项目
android·android studio·springboot·前后端分离·大作业·购物商城
这个家伙很笨6 小时前
了解Android studio 初学者零基础推荐(4)
android·ide·android studio
alexhilton8 小时前
在Android应用中实战Repository模式
android·kotlin·android jetpack
二流小码农12 小时前
鸿蒙开发:DevEcoTesting中的稳定性测试
android·ios·harmonyos
一起搞IT吧12 小时前
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
android·图像处理·数码相机
xzkyd outpaper12 小时前
Android中ContentProvider细节
android·计算机八股
恋猫de小郭13 小时前
Flutter 多版本管理工具 Puro ,它和 FVM 有什么区别?
android·前端·flutter