根据人眼视觉残留现象,连续播放一些列的图像,形成动画效果。
Android中的动画:
- 游戏:利用自定义View的绘制方法,开启线程频繁的刷新界面,形成动画;
- Android 视图动画:针对 View 以及View的子类。
- Android 属性动画:针对定义的属性,大部分可以操作View
补间动画
Android根据起始状态和结束状态,进行控件的移动、旋转、缩放等显示效果的变化,形成一个动画
实现方式
- XML 定义动画 (Android官方推荐)
- xml 定义在 res/anim/ 文件夹下面;
- 这种 anim动画,需要通过 AnimationUtils 来加载
代码实现动画(可扩展性差)
补间动画,就是指定开始状态,并且指定结束状态,Android系统自动的在这两个状态之间进行填充,形成连续的动画,就被称为补间动画
- 代码从XML中加载 动画对象;
- 给需要处理的控件,设置动画就可以播放了;
补间动画特点
- 通过指定时间、起始状态、结束状态,通过Android系统API,来进行动态效果的呈现;
- 补间动画,播放完成执行,控件将会还原为原始的位置、状态;
- 可以根据动画的播放情况,进行动画的播放状态的监听, 通过监听器可以监测:动画的开始、循环、停止;
- 当动画还在播放的时候,如果再次设置控件的动画,那么当前的动画将会终止;
关于动画的平滑
- Android系统,播放补间动画时,根据时间,起始状态、结束状态,生成动画效果
- 插值器 interpolator: Android系统根据插值器,在每一个等分的时间中,来设置不同的数值
组合变化
- translate + scale 在 中,写入多个动画效果就可以将多种效果同时组合在一起播放。
- 如果希望动画是串行执行,那么后续动画的起始播放时间,需要延后!!!
- 补间动画的 set 最终相当于线程池,内部的每一个动画效果实际上是由一个线程执行的,所有即使一个效果无限循环,其他效果也能够执行。
补间动画的重点
- !!! 任何动画的操作都不会影响实际控件的状态。因为实际上补间动画只是把控件的外观进行了各种处理,相当于控件还在原来的位置,还是原来的尺寸
- 尝试 translate + rotate 就可以重现控件状态。
- 补间动画 通过 AnimationUtils.loadAnimation 进行加载的。给控件 View startAnimation(Animation) 就可以执行控件的动画了。
关于动画的接口
动画播放的时候是有状态了,例如 开始动画、动画结束
AnimationListener 包含动画三种状态的回调:
- onAnimationStart 动画开始执行
- onAnimationEnd 动画执行完成
- onAnimationRepeat 动画重复执行。
几种动画效果
xml文件
res/anim/
旋转:
<!--
android:repeatCount="infinite" 循环次数:无限循环
android:interpolator="@android:interpolator/linear": 可以平滑旋转
-->
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
/>
移动:
<translate
android:fromXDelta="0"
android:toXDelta="200"/>
缩放:
<!--
缩放视图, 必须包含x, y的缩放设置
若不设置: 默认 0 -> 0
-->
<scale android:fromXScale="1"
android:toXScale="2"
android:fromYScale="1"
android:toYScale="2"
android:pivotX="50%"
android:pivotY="50%"/>
set集合
<?xml version="1.0" encoding="utf-8"?>
<!--
动画定义的集合, 使用set来包含多个动画
如果只有一个动画效果, 可以直接写动画的指令
duration: 持续时间
Delta: 增量
startOffset: 代表当前效果在动画启动多长时间之后 执行
补间动画实际上没有操作控件的属性
而是把控件的显示调整了 动画播放完成后还会还原原始状态
Android的坐标系, 左上角为圆点!!!
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--所有的动画效果, 都需要设置duration属性-->
<translate
android:duration="1000"
android:fromYDelta="0"
android:toYDelta="-300"
android:fromXDelta="0"
android:toXDelta="-300"
/>
<!--alpha 让控件逐渐消失和显示
startOffset 代表当前效果在动画启动多长时间之后执行 , 单位: 毫秒
-->
<alpha
android:duration="1000"
android:fromAlpha="1"
android:toAlpha="0"/>
</set>
java 代码文件
public void btnRotateClick(View view) {
//图片旋转
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_rotate);
animation.setRepeatMode(Animation.INFINITE);
//设置插值器 平滑旋转
animation.setInterpolator(new LinearInterpolator());
view.startAnimation(animation);
}
public void btMoveInterpolator(View view) {
//带有插值器的移动
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_move);
animation.setDuration(1000);
// animation.setInterpolator(new OvershootInterpolator(20));
animation.setInterpolator(new SimpleInterpolator());
view.startAnimation(animation);
}
public void btnScale(View view) {
ImageView imageView = (ImageView) findViewById(R.id.image_view);
if (imageView != null) {
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_heartbeat);
animation.setDuration(1000);
AnimationSet animationSet = (AnimationSet) animation;
List<Animation> animations = animationSet.getAnimations();
Animation anim = animations.get(0);
//设置第一次执行完成之后, 循环播放的次数
anim.setRepeatMode(Animation.REVERSE);
anim.setRepeatCount(Animation.INFINITE);
imageView.startAnimation(animation);
}
}
动画的监听
设置动画监听: 当动画开始, 完成, 重复时, 进行回调
animation.setAnimationListener(this);
//Animaition监听/start/
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
tvInc.setVisibility(View.INVISIBLE);
tvZan.setText("赞" + zanCount);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
//Animaition监听///end/
逐帧动画
- !!! 逐帧动画是一个 drawable 可以认为是一个图片;和 shape 相似都是用 xml 写出来的。
- 当中,从 xml 顺序加载图片,最上面的 最先显示。
- 可以指定 oneshoot 属性,控制动画是否只播放一次。如果为 true,那么只播放一次,即使播放完成, 如果不进行stop的话,依然是 running的状态。代表直接start()是不会执行的,只有stop之后才可以重新start
逐帧动画的使用
- 准备图片资源
- 在drawable中编写 xml,指定每一帧的显示图片内容
- ImageView 或者 其他View 的background属性,指定资源,方式:@drawable/名称
- 代码获取ImageView ,并且获取到 background ,检查是否是 AnimationDrawable ,进行强制类型转换
- AnimationDrawable start(), stop(); 进行播放的控制
- start() 不支持继续播放,每次从头开始。
应用场景
- 重复的,有动画效果的提示性内容,例如:京东客户端下拉刷新的时候“快递小哥”的动画
- 一些小的动画,不能够使用补间动画的情况,或者显示现实世界中的人物、动画的动态效果。
- 主要是配合一些事件的操作,如下拉刷新、圆圈进度条的处理
代码示例
<!--
逐帧动画, 就是在指定时间中, 依次显示每一张图片
形成连续的动画效果
android:oneshot="true"
如何显示是关键
-->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
>
<item android:duration="200" android:drawable="@mipmap/d00"/>
.....
<item android:duration="200" android:drawable="@mipmap/d09"/>
</animation-list>
public void btnPlayFrame(View view) {
//TODO: 播放动画
//1. 因为逐帧动画是图片, 所以要从ImageView中取出来, 才能进行控制
ImageView imageGift = (ImageView) findViewById(R.id.image_gift);
//获取src属性
Drawable drawable = imageGift.getDrawable();
//shape -> ShapeDrawable
//animation-list -> AnimationDrawable
if (drawable != null && drawable instanceof AnimationDrawable) {
//2. 播放动画
AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
animationDrawable.start();
}
// imageGift.setImageDrawable();
}
public void btnStopFrame(View view) {
//TODO: 停止动画
//1. 获取图像 逐帧动画
ImageView imageGift = (ImageView) findViewById(R.id.image_gift);
Drawable drawable = imageGift.getDrawable();
if (drawable != null && drawable instanceof AnimationDrawable) {
AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
//Stop会将动画暂停, 但是下一次, start的时候, 会从头开始播放
animationDrawable.stop();
//Gif 如何显示: GifDrawable库 WebView
}
}
属性动画
- 属性动画定义在 res/animator/ 这个资源目录下;
- 属性动画:实现按时间进行控件属性的变化;
- 实际改变控件的属性,整个布局重新排版,而且不会还原。
objectAnimator标记
- 能够以动画的方式修改任意对象的属性;
- 能够通过 setXxxx 这种方法的自动调用,来实现对属性“xxxx” 来进行修改;
- 这个标记可以通过 指定属性的名称,来进行设置。
原理
执行原理
- Animator 通过 xml 来加载,获取需要修改的属性的名称、类型、数值;
- AnimatorInflator 来加载xml, 之后设置了 setTarget(Object) ,指定需要修改属性的内容的对象;
- 通过将属性的名称转为 JavaBean 属性方法设置命名规则:属性名首字母大写,前面添加 set,例如 left属性,那么就会生成 setLeft(数值类型) 进行反射,查找指定对象中是否包含这个方法,如果包含,就可以按照指定的时间(duration) 进行 valueFrom 到 valueTo 这两值之间的进行动态的设置。
实现步骤
- 准备属性动画的XML动画资源;
- 准备需要播放动画的对象;
- 加载属性动画的 XML
- 设置动画要修改的对象
- 修改的属性名称,一定是 Java类文件中的 public void setXxxxx(… xxx) 的方法的 xxxx 这个属性,首字母小写
属性动画的Target的设置
- 比较常见的是 View
- 修改的属性都是 color, dimension, int, float
- 属性动画还可以指定非 View的对象。官方使用采用的自定义的Object,然后再Object setXxxx() 内部来实现更强大功能。
动画执行序列 < set>
set 标签可以 通过 ordering属性进行设置,内部的动画是否在一起执行
sequentially 串行执行,一个执行完,在执行另外的一个,不要有无限重复的,因为后边的没法执行了
together 并行一起执行
V4包动画支持
ViewCompat 支持 设置View的状态,例如 ViewCompat.setScaleX(View, float) 就是缩放控件水平方向的尺寸;
ViewPropertyAnimatorCompat 支持 实现低版本手机的属性动画功能,
ViewCompat.animate(View) 为View创建一个属性动画对象
动画与事件的配合
ScrollView 的 onTouch 中的 MOVE 事件可以和控件的隐藏显示配合
ListView OnScrollListener 可以检测ListView的滚动,滚动的过程中可以实现图片,标题的缩放
代码实例
res/animator/
<!--
设置对象属性修改器, 可以在指定的时间之内,
将指定的属性, 从一个数值变换到另一个数值
-->
<!-- setTranslationX(float x)-->
<objectAnimator
android:duration="1000"
android:propertyName="translationX"
android:valueType="floatType"
android:valueFrom="0dp"
android:interpolator="@android:interpolator/overshoot"
android:valueTo="200dp"/>
<objectAnimator
android:interpolator="@android:interpolator/linear"
android:duration="1000"
android:propertyName="rotation"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="360"/>
java
public void btnPropertyMove(View view) {
//属性动画的加载
//1. 加载器 加载xml文件
Animator animator = AnimatorInflater.loadAnimator(this,
R.animator.animator_move);
//2. 将需要修改的对象传递给属性动画
animator.setTarget(mTextView);
//3. 动画的播放
animator.start();
}
public void btnPropertyColor(View view) {
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator_color);
animator.setTarget(mTextView);
animator.start();
}
public void btnPropertyTextSize(View view) {
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator_textsize);
animator.setTarget(mTextView);
animator.start();
}
public void btnPropertyCustom(View view) {
//自定义对象的属性修改
//1. 自定义类对象: 确认属性名
TextShower shower = new TextShower(mTextView);
//2. 加载xml或者写代码
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.anmiator_text_show);
//3. 设置目标
animator.setTarget(shower);
animator.start();
}
public void btnPropertyCode(View view) {
//代码形式的属性动画
//所有的属性的数值,如果是位置那么以像素为单位
// ObjectAnimator.ofFloat(mTextView, "translationX", 0, 200).setDuration(2000).start();
//利用set 可以实现多个属性同时修改
AnimatorSet set = new AnimatorSet();
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mTextView, "translationX", 0, 400).setDuration(2000);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mTextView, "rotation", 0, 360).setDuration(200);
set.playTogether(animator1, animator2);
set.start();
}