1. 帧动画
1.1 简介
将一系列的图片按照顺序播放,每一张图片就是动画中的一帧,连续播放后就形成了动画,使用起来比较简单,缺点是当图片过多或者过大是,容易导致 OOM。
1.2 使用
- 把素材加入drawable,我这里只是为了测试效果,随便加的图片
在drawable中新建from.xml文件,用于将图片进行汇总
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/cat" android:duration="120" />
<item android:drawable="@drawable/cow" android:duration="120" />
<item android:drawable="@drawable/dog" android:duration="120" />
<item android:drawable="@drawable/horse" android:duration="120" />
</animation-list>
在activity中编写相关代码
public class FrameAnimationActivity extends AppCompatActivity implements View.OnClickListener {
private boolean flag;
private AnimationDrawable anim;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_frame_animation);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
ConstraintLayout constraintLayout = findViewById(R.id.main);
anim = (AnimationDrawable) constraintLayout.getBackground();
constraintLayout.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (flag){
anim.start();
flag = false;
}else {
anim.stop();
flag = true;
}
}
}
2. 属性动画
2.1 前言
是在 Android 3.0(API 11)才提供动画库。属性动画不仅可以使用自带的 API 来实现最常用的动画,而且通过自定义 View 的方式来做出定制化的动画,相比于 View 动画功能更强大。
补充:
- View 动画定义
动画变化分为 4 种,平移、缩放、旋转、透明度,通过这 4 种动画其中的一种变换或者组合变换,使视图完成一种渐进式的动画效果。
-
View动画的缺点
-
View 动画只能作用在单一视图上,即只对一个 Button、TextView、或者 ViewGroup,不能作用于非 View 对象的属性,如改变视图颜色属性、自定义 View 时的路径改变的动画效果等,这些通过 View 动画难以实现,通过属性动画可以很好的完成。
-
View 动画的效果只有 4 种,很难完成更复杂的动画效果。
-
View 动画不能改变控件的属性,如通过 View 动画移动一个 Button,移动后点击 Button 显示的位置,并不能触发点击事件,但是点击 Button 原来的位置,可以触发点击事件,可见 View 动画不能改变控件的属性,只是显示的效果改变了而已。
-
2.2 使用
按照常用的排序:
ViewPropertyAnimator --> ObjectAnimator --> ValueAnimator
当然这是单一动画的选择顺序,按照这个顺序使用起来会很方便,如果是使用 AnimationSet,组合动画中每个动画,一般使用 ObjectAnimator 来构建
2.2.1 ValueAnimator
- ofInt
方式一:代码编写
public class ValueAnimationActivity extends AppCompatActivity implements ValueAnimator.AnimatorUpdateListener, View.OnClickListener {
private TextView textView;
private ValueAnimator valueAnimator;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_value_animation);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
textView = findViewById(R.id.tv_value_animation);
//设置变化范围
valueAnimator = ValueAnimator.ofInt(16,48,60,16);
valueAnimator.addUpdateListener(this);
button = findViewById(R.id.btn_ofInt);
button.setOnClickListener(this);
}
//ofInt动画改变大小
@Override
public void onAnimationUpdate(@NonNull ValueAnimator animation) {
int animatedValue = (int) valueAnimator.getAnimatedValue();
textView.setTextSize(animatedValue);
}
@Override
public void onClick(View v) {
//设置变化时间
valueAnimator.setDuration(1000);
//开始变化
valueAnimator.start();
}
}
方式二:使用xml
在res/animato文件夹下创建value_animator.xml
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="4000"
android:fillAfter="false" //当动画结束后,是否保持动画的最终状态
android:fillBefore="true" //动画开始前是否保持动画开始时的状态
android:fillEnabled="true" //是否启用 fillBefore 和 fillAfter 属性
android:repeatCount="1" //动画重复的次数
android:repeatMode="restart" //动画重复时的模式 restart:动画从头开始重复;reverse:动画在重复时倒退播放
android:valueFrom="16" //
android:valueTo="48"
android:valueType="intType" />
然后加载动画,设置监听 AnimatorUpdateListener
valueAnimatorXml = (ValueAnimator)AnimatorInflater.loadAnimator(this, R.animator.value_animator);
valueAnimatorXml.setTarget(textView);
- ofObject
对于 ofInt 方法,没有估值器参数,实际上已经具备系统内置的估值器 IntEvaluator,内置的估值器已经实现从开始值到结束值的过渡过程,能够得到不同时刻的 int 值,同理,对于 ofFloat()方法,也内置了 FloatEvaluator。
对于 ofObject() 方法,系统没有提供估值器,因为系统不知道我们我们要传入的对象的类型,所以需要我们自己来实现一个估值器。
- 自定义一个类 MyPoint,通过 MyPoint 对象来表示控件的位置
public class MyPoint {
private int x;
private int y;
public MyPoint(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
- 然后实现一个估值器,通过这个估值器来得到从开始值到结束值之间的不同时刻下的值,其中 fraction 由插值器给出,代表动画的进度, startValue 和 endValue 表示开始值和结束值。
public class PointEvaluator implements TypeEvaluator<MyPoint> {
@Override
public MyPoint evaluate(float fraction, MyPoint startValue, MyPoint endValue) {
int x = (int) (fraction * (endValue.getX() - startValue.getX()) + startValue.getX());
int y = (int) (fraction * (endValue.getY() - startValue.getY()) + startValue.getY());
return new MyPoint(x, y);
}
}
- 接下来就可以使用自定义的估值器通过 ofObject 来构建 ValueAnimator,这里只给出通过代码实现的方式
valueAnimatorOfObject = ValueAnimator.ofObject(new PointEvaluator(),
new MyPoint(30,30), new MyPoint(400,400));
valueAnimatorOfObject.addUpdateListener(this);
button_of_object = findViewById(R.id.btn_ofObject);
button_of_object.setOnClickListener(this);
2.2.2 ObjectAnimator
ObjectAnimator.ofFloat(image_object, "rotation", 0, 360.0f)
.setDuration(4000)
.start();
这样就开启了动画,不需要像 ValueAnimator 一样设置更新监听,手动赋值并刷新 View。只需要给出需要更改的属性即可
| 属性 | 作用 | 数值类型 |
|---|---|---|
| Alpha | 控制View的透明度 | float |
| TranslationX | 控制X方向的位移 | float |
| TranslationY | 控制Y方向的位移 | float |
| ScaleX | 控制X方向的位移 | float |
| ScaleY | 控制Y方向的位移 | float |
| Ration | 控制以屏幕方向为轴的旋转度数 | float |
| RationX | 控制以X轴为轴的旋转度数 | float |
| RationY | 控制以Y轴为轴的旋转度数 | float |
只要将这些属性参数设置到 ObjectAnimator.ofFloat() 方法中,ObjectAnimator 就会根据属性参数找到对应的属性(前提是 该对象存在这个属性),然后进行自动赋值,实现动画效果。另外,我们还可以通过 ObjectAnimator 改变自定义 的属性,下面就来展示一下自定义属性的改变效果。
2.2.3 ViewPropertyAnimator
直接使用
TextView textView = findViewById(R.id.tv_view_property);
textView.animate()
.translationYBy(-100)
.alphaBy(-0.1f)
.scaleX(1.5f)
.rotationBy(180)
.setDuration(4000)
.start();
带By的表示在原有位置的基础上进行的偏移
2.2.4 AnimationSet
有时我们在改变一个控件或者视图时,可能需要改变多个属性的动画效果,而且不同的属性改变时的先后顺序,也有一定的要求,比如先拉伸,然后改变颜色,最后再旋转,这时就需要 AnimationSet 来完成一系列的动画组合。
AnimatorSet.play(Animator anim) // 播放当前动画
AnimatorSet.after(long delay) // 将现有动画延迟x毫秒后执行
AnimatorSet.with(Animator anim) // 将现有动画和传入的动画同时执行
AnimatorSet.after(Animator anim) // 将现有动画插入到传入的动画之后执行
AnimatorSet.before(Animator anim) // 将现有动画插入到传入的动画之前执行
AnimatorSet.playSequentially // 各个动画按照顺序执行,前一个执行完,后面的再开始执行
public class AnimationSetActivity extends AppCompatActivity implements View.OnClickListener {
private ObjectAnimator animator1;
private ObjectAnimator animator2;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_animation_set);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
ImageView image_animator_set = findViewById(R.id.image_animator_set);
animator1 = ObjectAnimator.ofFloat(image_animator_set, "scaleX", 1, 2)
.setDuration(2000);
animator2 = ObjectAnimator.ofFloat(image_animator_set, "rotation", 0, 450.0f)
.setDuration(3000);
findViewById(R.id.btn_animator_set).setOnClickListener(this);
}
@Override
public void onClick(View v) {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(animator1, animator2);
animatorSet.start();
}
}
2.2.5 估值器
估值器完成的工作就是给出不同进度下的值
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
2.2.6 插值器
插值器是来获取进度的,所有的插值器都是实现 TimeInterpolator 接口,随着时间的发展,给出不同时刻属性变化的百分比,这个百分比就是进度,插值器给出这个百分比之后,最后给到估值器,估值器通过这个百分比计算对应时刻的值
当需要改变插值器时,通过 setInterpolator(Interpolator interpolator) 设置 Interpolator, Interpolator 其实就是速度设置器,在参数里填入不同的 Interpolator ,动画就会以不同的速度模型来执行
AccelerateDecelerateInterpolator // 先加速再减速
LinearInterpolator // 匀速A
ccelerateInterpolator // 加速
DecelerateInterpolator // 持续减速直到 0
AnticipateInterpolator // 回拉一下再进行正常动画轨迹O
vershootInterpolator // 动画会超过目标值一些,然后再弹回来
AnticipateOvershootInterpolator // 上面这两个的结合版:开始前回拉,最后超过一些然后回弹
BounceInterpolator // 在目标值处弹跳
CycleInterpolator // 正弦 / 余弦曲线模型
PathInterpolator // 自定义动画完成度 / 时间完成度曲线。
FastOutLinearInInterpolator // 加速模型,曲线公式是用的贝塞尔曲线
FastOutSlowInInterpolator // 先加速再减速。用的是贝塞尔曲线
LinearOutSlowInInterpolator // 持续减速
2.2.7 动画监听
(1)基类 Animation 中有一个监听 AnimatorListener ,可以监听动画开始、结束、重复、取消时刻,从而来进行一系列操作。ObjectAnimator、ValueAnimator、AnimatorSet 都是继承自 Animation,所以都可以设置该监听。
(2) ValueAnimator 还有另外一个监听 ValueAnimator.AnimatorUpdateListener,在值变化时,通过该监听得到不同时刻的值,从而对对象设置值,改变对象属性。
(3)有时我们可能不需要监听动画个多个时刻,如仅仅需要监听结束时刻,然后执行我们想要执行的一个动作,那对于其他时刻就没必要重写,AnimatorListenerAdapter 就是来满足这个需求的,我们可以根据需要的监听时刻进行重写。
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener,
Animator.AnimatorPauseListener {
/**
* {@inheritDoc}
*/
@Override
public void onAnimationCancel(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationEnd(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationRepeat(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationStart(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationPause(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationResume(Animator animation) {
}
}
本系列【安卓基础重点知识】是刚开始学习android的时候记录的,其中部分内容来自网页,忘记记录来源了,如需添加引用,联系我即可