Android 动画实现

动画的分类

主要分类:

  • 传统动画(View Animation)
  • 帧动画(Drawable Animation)
  • 属性动画(Property Animation)

传统动画

传统动画即指的android3.0之前的动画实现. 只有界面的变化, 但是位置属性(即屏幕坐标)不发生改变.

关键类

  • Animation.AnimationListener 动画事件监听器

  • Interpolator 动画插值器

  • Animation 动画的抽象类

  • AnimationUtils 动画工具类

  • AnimationDrawable 帧动画

动画都是支持Java代码和XML布局两种方式创建动画效果的.

java类名xml关键字描述信息
AlphaAnimation<alpha> 放置在res/anim/目录下渐变透明度动画效果
RotateAnimation<rotate> 放置在res/anim/目录下画面转移旋转动画效果
ScaleAnimation<scale> 放置在res/anim/目录下渐变尺寸伸缩动画效果
TranslateAnimation<translate> 放置在res/anim/目录下画面转换位置移动动画效果
AnimationSet<set> 放置在res/anim/目录下一个持有其它动画元素alpha、scale、translate、rotate或者其它set元素的容器

Animation

Animation是所有的传统动画的抽象基类; 所以介绍下所有动画的通用属性和方法

属性方法描述
android:detachWallpapersetDetachWallpaper(boolean)是否允许在壁纸上运行
android:durationsetDuration(long)动画持续时间,毫秒为单位
android:fillAftersetFillAfter(boolean)控件动画结束时是否保持动画最后的状态
android:fillBeforesetFillBefore(boolean)控件动画结束时是否还原到开始动画前的状态
android:fillEnabledsetFillEnabled(boolean)与android:fillBefore效果相同
android:interpolatorsetInterpolator(Interpolator)设定插值器(指定的动画效果,譬如回弹等)
android:repeatCountsetRepeatCount(int)重复次数, 如果为1会执行两次动画
android:repeatModesetRepeatMode(int)重复模式
android:startOffsetsetStartOffset(long)调用start函数之后等待开始运行的时间,单位为毫秒
android:zAdjustmentsetZAdjustment(int)表示被设置动画的内容运行时在Z轴上的位置(top/bottom/normal),默认为normal

注意

  1. 类名和在XML中的标签名字不一样
  2. 如果FillAfter和FillBefore同时为true, After有效. 都不设置默认是Before效果

没有对应属性的Animation函数

void start ()
// 开始动画(只能在getTransition)

void cancel ()
// 结束动画(如果当前动画正在执行中)

void reset ()
// 如果结束了一个动画想再次开启动画

void setBackgroundColor (int bg)

void setInterpolator (Context context, 
                int resID)
// 加载resource中的interpolator文件
复制代码

设置动画事件的监听器

animation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
        // 动画开始
    }

    @Override
    public void onAnimationEnd(Animation animation) {
        // 动画结束
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        //动画重复, 如果执行setRepeat方法就会在每次加载动画的时候都回调该方法
    }
});
复制代码

View

关于设置View的动画函数

void setAnimation (Animation animation)
// 通过此函数设置的动画需要父容器调用inviliData()才会执行

void startAnimation (Animation animation)
// 设置动画并且立即执行

void clearAnimation ()

Animation getAnimation ()

void postInvalidateOnAnimation (int left, 
                int top, 
                int right, 
                int bottom)

void postInvalidateOnAnimation ()

void postOnAnimation (Runnable action)

void postOnAnimationDelayed (Runnable action, 
                long delayMillis)
复制代码

View自带的动画生命周期

void onAnimationStart ()

void onAnimationEnd ()
复制代码

AlphaAnimation

设置透明动画效果

xml属性解释
android:fromAlpha动画开始的透明度 百分比(0.0到1.0,0.0是全透明,1.0是不透明)
android:toAlpha动画结束的透明度,同上

构造函数创建动画

AlphaAnimation (float fromAlpha, 
                float toAlpha)

AlphaAnimation (Context context, 
                AttributeSet attrs)
复制代码

加载XML动画

 Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_set);
        mButton.startAnimation(animation);
复制代码

RotateAnimation

设置旋转动画效果

xml属性解释
android:fromDegrees旋转开始角度,正代表顺时针度数,负代表逆时针度数
android:toDegrees旋转结束角度,正代表顺时针度数,负代表逆时针度数
android:pivotX缩放起点X坐标
android:pivotY缩放起点Y坐标,同上规律

注意: pivot支持三种属性值, 其实还有一些其他动画也支持这三种属性值.往后看

  • 50

    表示以View的左上角偏移宽或高50像素点为原点

  • 50%

    表示以View的左上角偏移View自身宽或高的百分之五十的像素点为原点

  • 50%p

    表示以View的左上角偏移View的父容器宽或高的百分之五十的像素点为原点

同样可以直接使用构造方法在代码创建

RotateAnimation (Context context, 
                AttributeSet attrs)

RotateAnimation (float fromDegrees, 
                float toDegrees)

RotateAnimation (float fromDegrees, 
                float toDegrees, 
                float pivotX, 
                float pivotY)

RotateAnimation (float fromDegrees, 
                float toDegrees, 
                int pivotXType,  // 该参数就是为了实现XML属性中的三个值, 后面如果有构造方法有这个参数不再讲
                float pivotXValue, 
                int pivotYType, 
                float pivotYValue)
复制代码

关于pivotType值

  • Animation.RELATIVE_TO_SELF 相当与XML的50%
  • Animation.RELATIVE_TO_PARENT 相当于XML的50%p
  • Animation.ABSOLUTE 相当于50

TranslateAnimation

设置平移动画效果

属性:

分别对应平移的起始坐标和终点坐标, 同样支持三种属性值.

构造方法

TranslateAnimation (Context context, 
                AttributeSet attrs)

TranslateAnimation (float fromXDelta, 
                float toXDelta, 
                float fromYDelta, 
                float toYDelta)

TranslateAnimation (int fromXType, 
                float fromXValue, 
                int toXType, 
                float toXValue, 
                int fromYType, 
                float fromYValue, 
                int toYType, 
                float toYValue)
复制代码

关于type(和RotateAnimation类似):

  • Animation.RELATIVE_TO_SELF 相当与XML的50%
  • Animation.RELATIVE_TO_PARENT 相当于XML的50%p
  • Animation.ABSOLUTE 相当于50

ScaleAnimation

设置缩放动画效果

属性:

对应:

  1. 缩放起点坐标
  2. 缩放中心坐标
  3. 缩放终点坐标
  1. XML中不支持浮点数设置百分比(除alpha以外)
  2. XML中不支持Animation的属性代码补全
  3. ScaleAnimation在XML中支持DP值
  4. 在代码中设置百分比全部使用浮点数表示(1表示100%)

AnimationSet

可以设置多个动画效果同时播放

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

    <translate
        android:duration="2000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:repeatCount="-1"
        android:toXDelta="100"
        android:toYDelta="100" >
    </translate>

    <scale
        android:duration="2000"
        android:fromXScale="0"
        android:fromYScale="0"
        android:repeatCount="-1"
        android:toXScale="1"
        android:toYScale="1" >
    </scale>


    <rotate
        android:duration="2000"
        android:fromDegrees="0"
        android:pivotX="0"
        android:pivotY="100"
        android:repeatCount="-1"
        android:toDegrees="360" >
    </rotate>

    <alpha
        android:duration="2000"
        android:fromAlpha="1"
        android:repeatCount="-1"
        android:toAlpha="0" >
    </alpha>

</set>
复制代码

代码设置动画集合

Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_set);
mButton.startAnimation(animation);
复制代码

需要注意的是AnimationSet不支持repeatCount属性(也就是说不能直接支持循环动画). 不过可以通过上面这种给动画单独增加循环属性(代码设置重复播放也不行);

Tip: 视图动画集只支持同时播放多个动画. 不支持链式播放动画(除非自己监听动画完成)

通过函数添加动画

void addAnimation (Animation a)
复制代码

AnimationUtils

前面已经使用了该类进行加载xml动画. 现在着重介绍

主要作用

  • 加载xml动画/插值器
  • 提供几种动画效果

然后通过View.startAnimation()设置到视图上播放

// 当前动画已运行时间, 毫秒值单位
long currentAnimationTimeMillis () 

// 加载动画
Animation loadAnimation (Context context, 
                int id)

// 加载插值器
Interpolator loadInterpolator (Context context, 
                int id)

// 加载布局动画
LayoutAnimationController loadLayoutAnimation (Context context, 
                int id)
复制代码

生成自带的动画效果(上下左右/移出动画)

// 生成一个从左到原坐标的滑动且渐隐效果的动画
Animation makeInAnimation (Context c, 
                boolean fromLeft) // 是否从左边进入, 否则右边进入

// 生成一个从下到上的滑动渐隐效果动画
Animation makeInChildBottomAnimation (Context c)

// 生成一个渐隐动画并且向左/右移出界面的动画
Animation makeOutAnimation (Context c, 
                boolean toRight)
复制代码

帧动画

帧动画属于加载 Drawable 资源来播放动画,就像Gif图片一样一帧一帧加载多个连贯图片形成动画效果. 由于需要加载多张图片一般都体积占用比较大的且容易影响流畅度, 但是制作起来很方便效果也可以做的很复杂. 注意OOM内存溢出.

一帧:就是一张图片(Drawable)

帧动画对应类即AnimationDrawable, 是Drawable的子类, 所以可以作为View的背景设置(BackgroundDrawable).

示例

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  ButterKnife.bind(this);
mAnimationDrawable = new AnimationDrawable(); // 创建动画

// 添加帧图片
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_20_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_30_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_50_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_60_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_80_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_90_black_24dp), 200);

// 将动画对象设置给imageView对象作为背景, 你在布局文件中设置背景也行
mIvFrame.setBackground(mAnimationDrawable);

}

@OnClick({R.id.btn_start_animation, R.id.btn_stop_animation})
public void onClick(View view) {
  switch (view.getId()) {
    case R.id.btn_start_animation:
    	mAnimationDrawable.start(); // 开始动画
    break;
    case R.id.btn_stop_animation:
    	mAnimationDrawable.stop(); // 停止动画, 该方法会自动判断是否在运行动画
    break;
  }
}
复制代码

除了代码中创建还可以通过在Drawable目录中创建xml文件. 使用节点animation-list.

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_battery_20_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_30_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_50_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_60_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_80_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_90_black_24dp" android:duration="200"/>
</animation-list>
复制代码

通过xml创建后还是需要在代码中显示动画的.

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

  // AnimationDrawable是Drawable的子类, 可以把它看做一张图片. 就像Gif由多张图片组成一张图片一样
  mAnimationList = (AnimationDrawable) getDrawable(R.drawable.animation_frame);
  mIvFrame.setBackground(mAnimationList); // 还是一样把动画当作图片设为背景
  // mIvFrame.setImageDrawable(mAnimationDrawable); 当然不止是背景

}

@OnClick({R.id.btn_start_animation, R.id.btn_stop_animation})
public void onClick(View view) {
  switch (view.getId()) {
    case R.id.btn_start_animation:
      mAnimationList.start();
      break;
    case R.id.btn_stop_animation:
      mAnimationList.stop();
      break;
  }
}
复制代码
AnimationDrawable
添加帧
void addFrame (Drawable frame,  // 一帧的图片
                int duration) // 一帧所持续时间, 毫秒单位
复制代码
得到持续时间
int getDuration (int i)
复制代码
得到帧图片
Drawable getFrame (int index) // 得到逐帧动画中指定索引的图片Drawable对象, 索引从0开始
复制代码
得到总帧数
int getNumberOfFrames () // 就是你给逐帧动画设置了几张图片. 6张就是返回6.并不是从0开始
复制代码
填充xml

该方法是将xml文件填充进对象. 在Android5.0(API21)添加,用的很少.而且直接将xml设为背景更加好用. 几个参数涉及到自定义属性我会另开文章讲解.

void inflate (Resources r,  
                XmlPullParser parser, 
                AttributeSet attrs,  
                Resources.Theme theme)
复制代码
执行持续时间
void setOneShot (boolean oneShot) // 设置是否单次播放, 默认为false. 即永久循环执行
boolean isOneShot () // 是否是单次播放
复制代码
是否正在播放动画
boolean isRunning ()	
复制代码
开始和停止
void start () // 官方不建议在Activity的onCreate()内执行, 但是我执行没出啥问题
void stop () 
void run ()	// 也可以开始动画, 不过官方建议用start()
复制代码
重启和暂停动画

虽然名字是显示和隐藏, 但是动画的隐藏类似于暂停动画一样, 毕竟帧动画依附于View上终究还是会显示一帧在屏幕上. 所以我觉得更像暂停动画.

boolean setVisible (boolean visible, // 是否显示或者隐藏
                boolean restart) // 是否重新开始动画
复制代码

注意的是: restart为true或者从隐藏到显示都会导致逐帧动画的重新开始. 所以暂停后继续播放也不能实现.

布尔返回值: 如果当前状态与上次状态不一致则会返回true, 反正false. 例如: 从显示到隐藏为false, 重新开始为false.

插值器

Interpolator 动画插值器是用来控制动画的速度变化.

属性动画和视图动画都兼容同样的动画插值器.

关键类:

TypeEvaluator(类型估值器)

所有的插值器有一个顶级的接口: Interpolator

xml描述
AccelerateDecelerateInterpolator@android:anim/accelerate_decelerate_interpolator动画始末速率较慢,中间加速
AccelerateInterpolator@android:anim/accelerate_interpolator动画开始速率较慢,之后慢慢加速
AnticipateInterpolator@android:anim/anticipate_interpolator类似弹弓, 先往后拉然后移动到指定坐标;
AnticipateOvershootInterpolator@android:anim/anticipate_overshoot_interpolator类似上面, 但是向后拉的时候有阻尼效果;
BounceInterpolator@android:anim/bounce_interpolator到达指定值时会有弹起效果;
CycleInterpolator@android:anim/cycle_interpolator先到达指定位置然后, 后退等值, 然后再恢复到原有值;
DecelerateInterpolator@android:anim/decelerate_interpolator动画开始快然后慢
LinearInterpolator@android:anim/linear_interpolator动画匀速改变
OvershootInterpolator@android:anim/overshoot_interpolator向前弹出一定值之后回到原来位置
PathInterpolator路径新增,定义路径坐标后按照路径坐标来跑。

在android5.0后新增三个interpolator

  • FastOutLinearInInterpolator(加速插值器)

    和AccelerateInterpolator的区别在于初始加速更快

  • FastOutSlowInInterpolator(加速减速插值器)

    起始速度更快

  • LinearOutSlowInInterpolator(持续减速)

如果你不满足于系统自带的插值器可以实现TimeInterpolator接口自定义

插值器曲线图

关键词:

  • Anticipate 回拉, 即在开始进入动画轨迹之前先回拉以下
  • Overshoot 越界, 超出动画轨迹一定距离
  • Decelerate 减速
  • Accelerate 加速
  • Linear 均速
  • Bounce 弹力
  • Cycle 反复 相当于AccelerateDecelerateInterpolator效果反复执行

属性动画

之前不是说了传统动画播放位置不变只是视图发生改变吗. 在Android3.0以后为了解决这个问题Google就引入了属性动画. 动画效果的变化会导致控件的位置属性发生改变.

属性动画是基于反射的原理获取对象的属性字段修改(偏移,透明,旋转). 例如修改View的setXX()方法就需要传入XX作为参数(PropertyName), 所以并没有类似Animation的ScaleAnimation或者TranslateAnimation;

关键类:

  • Animator 抽象类
    • AnimatorSet 属性动画集
      • AnimatorSet.Builder 构建更复杂的动画集
    • ValueAnimator 属性动画
      • ObjectAnimator 对象动画
      • TimeAnimator 时间动画

其他类

  • AnimatorInflater 动画填充器
  • AnimatorListener 属性动画监听器
    • AnimatorListenerAdapter 属性动画监听器的适配器
  • AnimatorPauseListener 动画可见监听器

示例

需要在res/animator目录中创建XML文件

实际上属性动画放在anim目录下依旧能够加载运行, 但是会报警告

属性动画XML支持三种标签:(xml标签及对应的Java类)

  1. objectAnimator (ObjectAnimator)
  2. animator (ValueAnimator)
  3. set (AnimatorSet)

XML创建动画

属性动画的XML属性支持不多

但是对象动画支持的属性很多, 并且可以直接针对View进行动画播放;

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="2000"
                android:propertyName="rotationY"
                android:valueFrom="0"
                android:valueTo="360"
                android:valueType="floatType">
</objectAnimator>
复制代码

全部属性(全部支持代码补全, 是不是比Animation的XML用起来方便):

依次含义:

  1. 持续时间

  2. 插值器

  3. pathData 要求必须api21以上

  4. 属性名

    字符串类型的值, 填入View的setXXX方法中的XXX字符串

  5. 属性值

  6. propertyXName 要求必须api21以上

  7. propertyYName 要求必须api21以上

  8. 重复次数

  9. 重复模式

  10. 延迟时间

  11. 属性起始值

  12. 属性结束值

  13. 属性类型 (ColorType/FloatType/IntType/PathType)

pathData/propertyXName/propertyYName这三个属性是搭配使用; 同时都要求Api21以上才有效;

具体用法我也不知道;

加载XML文件到视图上

Animator animator = AnimatorInflater.loadAnimator(getApplicationContext(), 
                                                  R.animator.objectAnimator);
animator.setTarget(mImageView);
animator.start();
复制代码

代码

ObjectAnimator anim = ObjectAnimator.ofFloat(v, "scaleX", 2f);//x方向放大两倍
anim.setDuration(1000);//时长
anim.setRepeatCount(2);//重复次数
anim.setRepeatMode(ValueAnimator.REVERSE);//重复时相反的方向恢复到原来的样子
anim.setInterpolator(new AccelerateInterpolator());//设置插值器为加速度
anim.start();
复制代码

ValueAnimator同样支持在XML中创建动画配置, 用法一致, 但是支持的属性比较少;

Animator

这是所有属性动画的父抽象类, 并不能直接使用.

// 复制动画
Animator clone ()

// 延迟时间
void setStartDelay (long startDelay)
long getStartDelay ()

// 动画单次执行耗时
Animator setDuration (long duration)

// 动画全部完成所需时间,循环次数*单次耗时 如果是无限循环动画返回-1(字段:DURATION_INFINITE)
long getTotalDuration ()

// 设置插值器
void setInterpolator (TimeInterpolator value)

// 设置动画执行的目标
void setTarget (Object target)
复制代码
控制动画播放

控制动画的生命周期比Animation更加自由;

// 开始动画
void start ()
    
// 暂停动画
void pause ()

// 继续动画
void resume ()

void end ()
// 结束动画

void cancel ()
// 取消动画和结束动画的区别在于会多执行一个函数onCancel
复制代码

动画状态

// 是否不可见状态
boolean isPaused ()

// 动画当前是否正在运行
boolean isRunning ()

boolean isStarted ()
复制代码
生命周期监听器
// 添加动画监听器
void addListener (Animator.AnimatorListener listener)

// 添加动画可见监听器
void addPauseListener (Animator.AnimatorPauseListener listener)
    
// 得到全部的监听器
ArrayList<Animator.AnimatorListener> getListeners ()

// 删除全部监听器
void removeAllListeners ()
void removeListener (Animator.AnimatorListener listener)
void removePauseListener (Animator.AnimatorPauseListener listener)
复制代码

示例

animator.addListener(new Animator.AnimatorListener() {
  @Override public void onAnimationStart(Animator animation) {
	 // 动画开始
  }

  @Override public void onAnimationEnd(Animator animation) {
	// 动画结束
  }

  @Override public void onAnimationCancel(Animator animation) {
	// 动画被取消
  }

  @Override public void onAnimationRepeat(Animator animation) {
	// 动画重复
  }
});
复制代码
暂停和继续监听器

(AnimatorPauseListener) 在动画处于暂停或者继续状态回调的监听器

void onAnimationPause (Animator animation)

void onAnimationResume (Animator animation)
复制代码

添加监听器.看Animato的如下方法

void addPauseListener (Animator.AnimatorPauseListener listener)
复制代码

动画开始值和结束值

这两个方法会根据执行的具体是哪个类而有所区别.

例如ValueAnimator会无效,因为其没有属性信息. 而AnimationSet则会传递给子动画(当子动画没有设置值时).

ObjectAnimator则会根据属性名改变该属性值.

// 动画开始和结束值
void setupStartValues ()
void setupEndValues ()
复制代码

结束或者取消动画, 只是回调方法存在不一致而已

// 结束动画
void end ()

// 取消动画
void cancel ()
复制代码

都可以取消动画, 但是在动画监听器的回调方法不一致

  • end

    回调onAnimationEnd方法

  • cancel

    回调onAnimationEnd和onAnimtionCacel方法

ValueAnimator

这只是一种值运算, 提供更加灵活的过渡效果

例如在1000毫秒内 由开始值0到结束值300的过渡变化(变化速率遵守默认插值器AccelerateDecelerateInterpolator) 当然你也可以自己设置插值器.

ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 100f, 300f);

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override public void onAnimationUpdate(ValueAnimator animation) {
    Log.i("日志", "值变化: "+ animation.getAnimatedValue());
  }
});

valueAnimator.start();
复制代码

输出结果:

ValueAnimator可以理解为拥有动画的特点(duration/interpolator), 只是提供计算一个平滑的动画过渡值而已, 但是如果改变对象的什么属性(边距/透明度/颜色值/宽高)就全凭你自定义了. 这就是为什么说属性动画可以控制的内容是任意对象了.

ofFloat方法填入参数是一个可变参数, 只要填入固定值, 必定会以第一个参数开始最后一个参数结尾. 然后中间的参数会运行到.

通过ofxx()等方法来创建ValueAnimator实例. 这一系列方法会产生默认的值过渡效果.

ValueAnimator ofArgb (int... values)

ValueAnimator ofFloat (float... values)

ValueAnimator ofInt (int... values)

ValueAnimator ofObject (TypeEvaluator evaluator, 
                Object... values)

// 通过属性值固定器来创建
ValueAnimator ofPropertyValuesHolder (PropertyValuesHolder... values)
复制代码

属性值变化监听器

void addUpdateListener (ValueAnimator.AnimatorUpdateListener listener)

void removeUpdateListener (ValueAnimator.AnimatorUpdateListener listener)

void removeAllUpdateListeners ()
复制代码

ValueAnimator的监听器中会回调参数ValueAnimator可以通过此来获取动画不断变化的属性值;

// 得到动画运行过程中不断变化的属性值
Object getAnimatedValue ()

// 根据属性名来获取属性值
Object getAnimatedValue (String propertyName)

// 得到一个属性值固定器
PropertyValuesHolder[] getValues ()
复制代码

百分比(fraction)这个在ValueAnimator中代表完成度.


// 范围0~1, 当前时间在总时间的进度百分比
float getAnimatedFraction ()
复制代码

动画播放时长

long getCurrentPlayTime ()
// 当前动画已播放时长

long getTotalDuration ()
// 动画全部播放完所需时长

long getFrameDelay ()
复制代码
TypeEvaluator

通过ValueAnimator的setEvaluator()方法设置TypeEvaluator

void setEvaluator (TypeEvaluator value)
复制代码

和插值器interpolator控制的速度变化不同的是 , 类型估值器, 主要是控制ValueAnimator的开始值和起始值的具体值.

其内部方法返回值决定了getAnimatedValue()的值.

默认提供的TypeEvaluator

  • TypeEvaluator:用户自定义属性值需要实现的接口
  • IntEvaluator:整数属性值
  • FloatEvaluator:浮点数属性值
  • ArgbEvaluator:十六进制color属性值
  • RectEvaluator: 矩形属性值

在api21后新增类型估值器

  • PointFEvaluator: 坐标属性值
  • FloatArrayEvaluator
  • IntArrayEvaluator
// 自定义 HslEvaluator
private class HsvEvaluator implements TypeEvaluator<Integer> {  
   float[] startHsv = new float[3];
   float[] endHsv = new float[3];
   float[] outHsv = new float[3];

   @Override
   public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
       // 把 ARGB 转换成 HSV
       Color.colorToHSV(startValue, startHsv);
       Color.colorToHSV(endValue, endHsv);

       // 计算当前动画完成度(fraction)所对应的颜色值
       if (endHsv[0] - startHsv[0] > 180) {
           endHsv[0] -= 360;
       } else if (endHsv[0] - startHsv[0] < -180) {
           endHsv[0] += 360;
       }
       outHsv[0] = startHsv[0] + (endHsv[0] - startHsv[0]) * fraction;
       if (outHsv[0] > 360) {
           outHsv[0] -= 360;
       } else if (outHsv[0] < 0) {
           outHsv[0] += 360;
       }
       outHsv[1] = startHsv[1] + (endHsv[1] - startHsv[1]) * fraction;
       outHsv[2] = startHsv[2] + (endHsv[2] - startHsv[2]) * fraction;

       // 计算当前动画完成度(fraction)所对应的透明度
       int alpha = startValue >> 24 + (int) ((endValue >> 24 - startValue >> 24) * fraction);

       // 把 HSV 转换回 ARGB 返回
       return Color.HSVToColor(alpha, outHsv);
   }
}

ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xff00ff00);  
// 使用自定义的 HslEvaluator
animator.setEvaluator(new HsvEvaluator());  
animator.start();  
复制代码

ObjectAnimator

由于ValueAnimator并不能直接控制View的动画(只能在监听器中手动设置); 而View加载动画时最常见的的操作, 所以Google重写了ValueAnimator就出现了ObjectAnimator. 一个可以通过反射直接修改View对象的属性的类;

正是因为其反射的原理所以需要set<PropertyName>()以及get<PropertyName>()的属性才能修改.

如果没有setter和getter函数有三种解决办法:

  • 继承View添加这两个函数
  • 使用一个包装类
  • 使用ValueAnimator替代

Tip: 如果指定了开始值可以不用拥有get<PropertyName>()方法

创建动画

同样是ofxx()函数创建动画但是和ValueAniamtor很大的区别就是支持直接setTarget设置目标;

颜色

该函数限制api21

ObjectAnimator ofArgb (Object target, 
                String propertyName, 
                int... values)

ObjectAnimator ofArgb (T target, 
                Property<T, Integer> property, 
                int... values)
复制代码

ObjectAnimator设置属性值的时候还有一个类型问题. 就是对象的该属性是什么类型, 你在ObjectAnimator中指定开始值和起始值的时候就要用什么类型. 否则将没有效果.

自定义对象
ObjectAnimator ofObject (T target, 
                Property<T, V> property, 
                TypeEvaluator<V> evaluator, 
                V... values)

ObjectAnimator ofObject (Object target, 
                String propertyName, 
                TypeConverter<PointF, ?> converter, 
                Path path)
// 限制api21

ObjectAnimator ofObject (T target, 
                Property<T, V> property, 
                TypeConverter<PointF, V> converter, 
                Path path)

ObjectAnimator ofObject (T target, 
                Property<T, P> property, 
                TypeConverter<V, P> converter, 
                TypeEvaluator<V> evaluator, 
                V... values)

ObjectAnimator ofObject (Object target, 
                String propertyName, 
                TypeEvaluator evaluator, 
                Object... values)
复制代码
ObjectAnimator ofFloat (Object target, 
                String xPropertyName, 
                String yPropertyName, 
                Path path)

ObjectAnimator ofFloat (T target, 
                Property<T, Float> property, 
                float... values)

ObjectAnimator ofFloat (T target, 
                Property<T, Float> property, 
                float... values)

ObjectAnimator ofFloat (Object target, 
                String propertyName, 
                float... values)
复制代码
int值
ObjectAnimator ofInt (T target, 
                Property<T, Integer> xProperty, 
                Property<T, Integer> yProperty, 
                Path path)

ObjectAnimator ofInt (T target, 
                Property<T, Integer> property, 
                int... values)

ObjectAnimator ofInt (Object target, 
                String propertyName, 
                int... values)

ObjectAnimator ofInt (Object target, 
                String xPropertyName, 
                String yPropertyName, 
                Path path)
复制代码
Multi

所有函数限制api21

ObjectAnimator ofMultiFloat (Object target, 
                String propertyName, 
                float[][] values)

ObjectAnimator ofMultiFloat (Object target, 
                String propertyName, 
                Path path)

ObjectAnimator ofMultiFloat (Object target, 
                String propertyName, 
                TypeConverter<T, float[]> converter, 
                TypeEvaluator<T> evaluator, 
                T... values)

ObjectAnimator ofMultiInt (Object target, 
                String propertyName, 
                int[][] values)

ObjectAnimator ofMultiInt (Object target, 
                String propertyName, 
                Path path)

ObjectAnimator ofMultiInt (Object target, 
                String propertyName, 
                TypeConverter<T, int[]> converter, 
                TypeEvaluator<T> evaluator, 
                T... values)
复制代码
PropertyValuesHolder

ObjectAnimator和ValueAnimator都拥有一个设置PropertyValuesHolder方法;

能保存多个动画属性并且动画播放时可以同时执行多个动画;

ObjectAnimator ofPropertyValuesHolder (Object target, 
                PropertyValuesHolder... values)

ValueAnimator ofPropertyValuesHolder (PropertyValuesHolder... values)
复制代码

介绍下PropertyValuesHolder方法. 可变参数的使用和ObjectAnimator一样.

PropertyValuesHolder ofFloat (String propertyName, 
                float... values)

PropertyValuesHolder ofInt (String propertyName, 
                int... values)

PropertyValuesHolder ofMultiFloat (String propertyName, 
                float[][] values)

PropertyValuesHolder ofMultiInt (String propertyName, 
                int[][] values)
复制代码

示例:

PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 300);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(pvh1, pvh2, pvh3).setDuration(1000).start();
复制代码

PropertyValueHolder还可以在XML标签中定义

<animator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="1000"
                android:repeatCount="1"
                android:repeatMode="reverse">
    <propertyValuesHolder android:propertyName="x" android:valueTo="400"/>
    <propertyValuesHolder android:propertyName="y" android:valueTo="200"/>
</animator>
复制代码
KeyFrame

PropertyValuesHolder还可以设置关键帧来控制动画播放的进度;

可以将属性拆成多段执行;

PropertyValuesHolder ofKeyframe (String propertyName, 
                Keyframe... values)

PropertyValuesHolder ofKeyframe (Property property, 
                Keyframe... values)

void setKeyframes (Keyframe... values)
复制代码

Property属于包含name和type的属性对象

XML设置

<propertyValuesHolder android:propertyName="progress" >
    <keyframe/>
    <keyframe android:fraction="0.2"
              android:interpolator="@android:anim/accelerate_interpolator"
              android:value="300"/>
    <keyframe android:interpolator="@android:anim/accelerate_interpolator"
              android:value="1000" />
</propertyValuesHolder>
复制代码

KeyFrame函数

static Keyframe	ofFloat(float fraction)

static Keyframe	ofInt(float fraction)
    
static Keyframe	ofObject(float fraction)
    
    
static Keyframe	ofFloat(float fraction, float value)

static Keyframe	ofInt(float fraction, int value)

static Keyframe	ofObject(float fraction, Object value)
复制代码
abstract Keyframe	clone()

float	getFraction()

Class	getType()
// 值类型

abstract Object	getValue()
// 取值

boolean	hasValue()
// 是否存在值
    
void	setFraction(float fraction)
// 设置完成百分比

void	setInterpolator(TimeInterpolator interpolator)
TimeInterpolator	getInterpolator()

abstract void	setValue(Object value)
复制代码
路径动画

ObjectAnimator还可以实现动画播放路径控制

根据绘制的路径Path移动控件动画. 属于ObjectAnimator动画的扩展.

// 触发点击事件
public void startAnimator(View view) {
    // 绘制路径
    Path path = new Path();
    path.moveTo(100, 100);
    path.lineTo(200, 100);
    path.lineTo(200, 200);
    path.lineTo(300, 300);
    path.lineTo(50, 50);
    
    // 创建浮动属性动画
    ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
    animator.setInterpolator(new AccelerateDecelerateInterpolator());
    animator.setDuration(3000);
    animator.start();
}
复制代码

AnimatorSet

属性动画集合控制子动画的播放时机比视图动画集合更加灵活自如; 继承Animator拥有属性动画基本功能;

XML标签

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
    <objectAnimator
        android:duration="3000"
        android:propertyName="rotationX"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:startOffset="0"
        android:valueFrom="0"
        android:valueTo="90"
        android:valueType="intType" />

</set>
复制代码

set标签只拥有一个属性, android:ordering 表示集合中的动画执行的时机, sequentially 依次执行 together 同时执行

然后引用标签

Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator_set);
animator.setTarget(mButton);
animator.start();
复制代码

代码中同样可以如此设置

void playTogether (Animator... items)

void playSequentially (Animator... items)
复制代码

这两个方法的参数都是可变参数, 可以传入N个动画对象来播放.

以下是两个属性动画集示例

同时播放

    AnimatorSet set = new AnimatorSet();
    set.playTogether(
            ObjectAnimator.ofFloat(v, "x", startBounds.left, finalBounds.left))
            .with(ObjectAnimator.ofFloat(v, "y", startBounds.top, finalBounds.top))
            .with(ObjectAnimator.ofFloat(v, "scaleX", startScale, 1f))
            .with(ObjectAnimator.ofFloat(v, "scaleY", startScale, 1f));

    set.setDuration(200);
    set.setInterpolator(new DecelerateInterpolator());
    set.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            //
        }

        @Override
        public void onAnimationCancel(Animator animation) {
            //
        }
    });
    set.start();
复制代码

顺序播放

    Animator scaleXAnimator = ObjectAnimator.ofFloat(mImageView, "scaleX", 1, 0.5f);
    scaleXAnimator.setDuration(2000);
    Animator scaleYAnimator = ObjectAnimator.ofFloat(mImageView, "scaleY", 1, 0.5f);
    scaleYAnimator.setDuration(2000);
    Animator rotationXAnimator = ObjectAnimator.ofFloat(mImageView, "rotationX", 0, 360);
    rotationXAnimator.setDuration(2000);
    Animator rotationYAnimator = ObjectAnimator.ofFloat(mImageView, "rotationY", 0, 360);
    rotationYAnimator.setDuration(2000);
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(scaleXAnimator)
            .with(scaleYAnimator)
            .before(rotationXAnimator)
            .after(rotationYAnimator);
    animatorSet.start();
复制代码

全部函数

// 依次播放和顺序播放N个动画
void playSequentially (List<Animator> items)
void playSequentially (Animator... items)
void playTogether (Collection<Animator> items)
void playTogether (Animator... items)



void setupStartValues ()
void setupEndValues ()
复制代码
AnimatorSet.Builder

属性动画集合有一个构造器, 可以用于创建顺序执行的动画集合

AnimatorSet.Builder play (Animator anim) // 创建一个构造器
复制代码

构造器函数

AnimatorSet.Builder	after(long delay)
// 延迟播放 

AnimatorSet.Builder	after(Animator anim)
// 向后添加动画

AnimatorSet.Builder	before(Animator anim)
// 向前添加动画

AnimatorSet.Builder	with(Animator anim)
// 同时播放动画
复制代码

下面是官方文档示例:

执行顺序:

  1. 最先播放 bounceAnim.
  2. 同时播放 squashAnim1, squashAnim2, stretchAnim1, and stretchAnim2
  3. 然后播放 bounceBackAnim.
  4. 最后播放 fadeAnim.
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();
复制代码

AnimatorInflater

和View动画(使用一个AnimationUtils工具类)不同的是属性动画使用一个填充器来加载XML动画资源.

只有两个静态方法.

Animator loadAnimator (Context context, 
                int id)

StateListAnimator loadStateListAnimator (Context context, 
                int id)
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值