参考博客:https://blog.youkuaiyun.com/carson_ho/article/details/73087488
一 帧动画
1. 作用对象
视图控件(
View
)
- 如
Android
的TextView、Button
等等- 不可作用于
View
组件的属性,如:颜色、背景、长度等等
2. 原理
- 将动画拆分为 帧 的形式,且定义每一帧 = 每一张图片
- 逐帧动画的本质:按序播放一组预先定义好的图片
工具:GifSplitter可拆分gif得到图片
这里只使用java动态设置动画,并没有使用xml配置。
public class FrameAnimActivity extends AppCompatActivity {
private ImageView mImageView;
private AnimationDrawable mAnimationDrawable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_frame_anim);
initView();
initAnim();
}
private void initView() {
mImageView = findViewById(R.id.frame_anim);
mAnimationDrawable = new AnimationDrawable();
}
private void initAnim() {
for (int i = 0; i < 44; i++) {
int id = getResources().getIdentifier("a" + (i + 1), "drawable", getPackageName());
Drawable drawable = getResources().getDrawable(id);
mAnimationDrawable.addFrame(drawable, 100);
}
mAnimationDrawable.setOneShot(false);
mImageView.setImageDrawable(mAnimationDrawable);
mAnimationDrawable.start();
}
}
效果图:录的屏上传不了,只能贴gif了,一样的结果。
- 优点:使用简单、方便
- 缺点:容易引起
OOM
,因为会使用大量 & 尺寸较大的图片资源应用场景:较为复杂的个性化动画效果。
二:补间动画
1.作用对象
视图控件(
View
)
- 如
Android
的TextView、Button
等等- 不可作用于
View
组件的属性,如:颜色、背景、长度等等2.原理
通过确定开始的视图样式 & 结束的视图样式、中间动画变化过程由系统补全来确定一个动画。
根据不同的动画效果,补间动画分为4种动画:
- 平移动画(
Translate
)- 缩放动画(
scale
)- 旋转动画(
rotate
)- 透明度动画(
alpha
)
demo:此处只使用动态设置的方式,录屏上传不了,所以只贴代码了。
public class TweenActivity extends AppCompatActivity {
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tween);
mButton = findViewById(R.id.translate_btn);
}
@Override
protected void onResume() {
super.onResume();
animSet();
}
private void translate() {
TranslateAnimation animation = new TranslateAnimation(0, 500, 0, 500);
animation.setDuration(3000);
animation.setRepeatCount(Animation.INFINITE);
mButton.startAnimation(animation);
}
private void scale() {
ScaleAnimation scaleAnimation = new ScaleAnimation(0, 2, 0, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(3000);
scaleAnimation.setRepeatCount(Animation.INFINITE);
mButton.startAnimation(scaleAnimation);
}
private void rotate() {
RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(3000);
rotateAnimation.setRepeatCount(Animation.INFINITE);
mButton.startAnimation(rotateAnimation);
}
private void alpha() {
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
alphaAnimation.setDuration(3000);
alphaAnimation.setRepeatCount(Animation.INFINITE);
mButton.startAnimation(alphaAnimation);
}
private void animSet() {
AnimationSet animationSet = new AnimationSet(true);
TranslateAnimation animation = new TranslateAnimation(0, 500, 0, 0);
animation.setDuration(3000);
animation.setRepeatCount(Animation.INFINITE);
ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1, 0, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(3000);
scaleAnimation.setRepeatCount(Animation.INFINITE);
RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(3000);
rotateAnimation.setRepeatCount(Animation.INFINITE);
AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0.5f);
alphaAnimation.setDuration(3000);
alphaAnimation.setRepeatCount(Animation.INFINITE);
animationSet.addAnimation(animation);
animationSet.addAnimation(scaleAnimation);
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(alphaAnimation);
mButton.startAnimation(animationSet);
}
}
三 属性动画
1. 背景
Android
系统一开始就提供了两种实现动画的方式:逐帧动画(Frame Animation
)补间动画(Tweened animation
)逐帧动画 & 补间动画存在一定的缺点:
即补间动画 只能够作用在视图
View
上,即只可以对一个Button
、TextView
、甚至是LinearLayout
、或者其它继承自View
的组件进行动画操作,但无法对非View
的对象进行动画操作
- 有些情况下的动画效果只是视图的某个属性 & 对象而不是整个视图;
- 如,现需要实现视图的颜色动态变化,那么就需要操作视图的颜色属性从而实现动画效果,而不是针对整个视图进行动画操作
- 补间动画只是改变了
View
的视觉效果,而不会真正去改变View
的属性。- 如,将屏幕左上角的按钮 通过补间动画 移动到屏幕的右下角
- 点击当前按钮位置(屏幕右下角)是没有效果的,因为实际上按钮还是停留在屏幕左上角,补间动画只是将这个按钮绘制到屏幕右下角,改变了视觉效果而已。
- 补间动画只能实现平移、旋转、缩放 & 透明度这些简单的动画需求
- 一旦遇到相对复杂的动画效果,即超出了上述4种动画效果,那么补间动画则无法实现。
- 即在功能 & 可扩展性有较大局限性
为了解决补间动画的缺陷,在
Android 3.0
(API 11)开始,系统提供了一种全新的动画模式:属性动画(Property Animation
)2.特点
- 作用对象进行了扩展:不只是View对象,甚至没对象也可以
- 动画效果:不只是4种基本变换,还有其他动画效果
- 作用领域:API11后引入的
3.工作原理
- 在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果
可以是任意对象的任意属性
- 属性动画有两个非常重要的类:
ValueAnimator
类 &ObjectAnimator
类- 其实属性动画的使用基本都是依靠这两个类
4.使用
1 ValueAnimator类
- 定义:属性动画机制中 最核心的一个类
- 实现动画的原理:通过不断控制 值 的变化,再不断 手动 赋给对象的属性,从而实现动画效果。
ValueAnimator
类中有3个重要方法:
ValueAnimator.ofInt(int values)
ValueAnimator.ofFloat(float values)
ValueAnimator.ofObject(int values)
估值器(TypeEvaluator),可继承自定义估值器。
- 作用:设置动画 如何从初始值 过渡到 结束值 的逻辑
- 插值器(
Interpolator
)决定 值 的变化模式(匀速、加速等)- 估值器(
TypeEvaluator
)决定 值 的具体变化数值
ValueAnimator.ofInt()
实现了 **将初始值 以浮点型的形式 过渡到结束值 ** 的逻辑,那么这个过渡逻辑具体是怎么样的呢?- 其实是系统内置了一个
IntEvaluator
估值器,内部实现了初始值与结束值 以浮点型的过渡逻辑
public class MainActivity extends AppCompatActivity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
valueAnim();
}
private void initView() {
mTextView = findViewById(R.id.text);
}
private void valueAnim() {
ValueAnimator valueAnimator = ValueAnimator.ofInt(mTextView.getLayoutParams().width, 500);
valueAnimator.setDuration(9000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (Integer) animation.getAnimatedValue();
mTextView.getLayoutParams().width = value;
mTextView.requestLayout();
}
});
valueAnimator.start();
}
}
2 ObjectAnimator类
直接对对象的属性值进行改变操作,从而实现动画效果
- 如直接改变
View
的alpha
属性 从而实现透明度的动画效果- 继承自
ValueAnimator
类,即底层的动画实现机制是基于ValueAnimator
类
- 本质原理: 通过不断控制 值 的变化,再不断 自动 赋给对象的属性,从而实现动画效果。
ObjectAnimator
与ValueAnimator
类的区别:
ValueAnimator
类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作;ObjectAnimator
类是先改变值,然后 自动赋值 给对象的属性从而实现动画;是 直接 对对象属性进行操作;自动赋给对象的属性的本质是调用该对象属性的set() & get()方法进行赋值
- 所以,
ObjectAnimator.ofFloat(Object object, String property, float ....values)
的第二个参数传入值的作用是:让ObjectAnimator
类根据传入的属性名 去寻找 该对象对应属性名的set() & get()
方法,从而进行对象属性值的赋值
3 .组合动画
- 单一动画实现的效果相当有限,更多的使用场景是同时使用多种动画效果,即组合动画
- 实现 组合动画 的功能:
AnimatorSet
类AnimatorSet.play(Animator anim) :播放当前动画
AnimatorSet.after(long delay) :将现有动画延迟x毫秒后执行
AnimatorSet.with(Animator anim) :将现有动画和传入的动画同时执行
AnimatorSet.after(Animator anim) :将现有动画插入到传入的动画之后执行
AnimatorSet.before(Animator anim) : 将现有动画插入到传入的动画之前执行4. ViewPropertyAnimator用法
- 从上面可以看出,属性动画的本质是对值操作
- 但
Java
是面向对象的,所以ViewPropertyAnimator
类使用解析
View.animate().xxx().xxx();
// ViewPropertyAnimator的功能建立在animate()上
// 调用animate()方法返回值是一个ViewPropertyAnimator对象,之后的调用的所有方法都是通过该实例完成
// 调用该实例的各种方法来实现动画效果
// ViewPropertyAnimator所有接口方法都使用连缀语法来设计,每个方法的返回值都是它自身的实例
// 因此调用完一个方法后可直接连缀调用另一方法,即可通过一行代码就完成所有动画效果5.监听动画
Animation
类通过监听动画开始 / 结束 / 重复 / 取消时刻来进行一系列操作,如跳转页面等等- 通过在
Java
代码里addListener()
设置Animation.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animation animation) {
//动画开始时执行
}
@Override
public void onAnimationRepeat(Animation animation) {
//动画重复时执行
}@Override
public void onAnimationCancel()(Animation animation) {
//动画取消时执行
}
@Override
public void onAnimationEnd(Animation animation) {
//动画结束时执行
}
});// 特别注意:每次监听必须4个方法都重写。
动画适配器AnimatorListenerAdapter
- 背景:有些时候我们并不需要监听动画的所有时刻
- 问题:但
addListener(new AnimatorListener())
监听器是必须重写4个时刻方法,这使得接口方法重写太累赘- 解决方案:采用动画适配器(
AnimatorListenerAdapter
),解决实现接口繁琐 的问题anim.addListener(new AnimatorListenerAdapter() {
// 向addListener()方法中传入适配器对象AnimatorListenerAdapter()
// 由于AnimatorListenerAdapter中已经实现好每个接口
// 所以这里不实现全部方法也不会报错
@Override
public void onAnimationStart(Animator animation) {
// 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
}
});
public class ObAnimActivity extends AppCompatActivity {
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ob_anim);
initView();
initAnim();
}
private void initView() {
mButton = findViewById(R.id.obj_anim_btn);
}
private void initAnim() {
float translationX = mButton.getTranslationX();
ObjectAnimator translation = ObjectAnimator.ofFloat(mButton, "translationX", translationX, 300, translationX);
ObjectAnimator rotation = ObjectAnimator.ofFloat(mButton, "rotation", 0, 360);
ObjectAnimator scale = ObjectAnimator.ofFloat(mButton, "scaleX", 1, 2, 1);
ObjectAnimator alpha = ObjectAnimator.ofFloat(mButton, "alpha", 1, 0, 1);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(translation).with(rotation).before(scale).after(alpha);
animatorSet.setDuration(5000);
animatorSet.start();
}
}
4.总结
属性动画的本质原理:通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果;
![]()