1.Android中的三种动画

  1. View动画

通过场景里的对象不断做图像变换(平移,缩放,旋转,透明度)从而产生动画效果,是一种渐近式动画,并支持自定义。

  1. 帧动画

帧动画其实也属于View动画。通过顺序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画效果,但图片过多过大会导致OOM

  1. 属性动画

属相动画通过动态地改变对象的属性从而达到动画效果。

重点在于属性动画的学习


2.View动画

View动画的作用对象是View。支持四种典型动画效果:

  • 平移动画 

TranslateAnimation

  • 缩放动画 

ScaleAnimation

  • 旋转动画 

RotateAnimation

  • 透明度动画 

AlphaAnimation

 

对于View动画,建议采用xml来定义动画,这样可读性更好

View动画的四种变换

名称

标签

子类

效果

平移动画

<translate>

TranslateAnimation

移动View

缩放动画

<scale>

ScaleAnimation

放大或缩小View

旋转动画

<rotate>

RotateAnimation

旋转View

透明度动画

<alpha>

AlphaAnimation

改变View的透明度

Animation属性:

xml属性

jav代码

作用

android:detachWallpaper

setDetachWallpaper(boolean)

是否在壁纸上运行

android:duration

setDuration(long)

动画的持续时间

android:fillAfter

setFillAfter(boolean)

动画结束后是否停留在结束位置

android:fillBefore

setFillBefore(boolean)

动画结束时是否还原开始位置

android:fillEnabled

setFillEnabled(boolean)

同上,与fillBefore相同

android:interpolator

setInterpolator(Interpolator)

设置插值器

android:repeatCount

setRepeatCount(int)

重复次数

android:repeatMode

setRepeatMode(int)

有两种重复类型,reverse倒序回放,restart从头播放

android:startOffset

setStartOffset(long)

开启动画startAnimation(animation)之后等待执行运行动画的时间

android:zAdjustment

setZAdjustment(int)

表示被设置动画的内容运行时在Z轴上的位置(top/bottom/normal),默认为normal

View动画既可以是单个动画,也可以是一些列动画组成。


<set> 标签标示动画集合,对应于AnimationSet类,可以包含若干动画,也可以嵌套其他的动画集合。

android:interpolator

  • 动画集合所采用的的插值器,插值器影响动画的速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。默认为

@android:anim/accelerate_decelerate_interpolator

  • ,即加速加速插值器。

android:shareInterpolator

  • 集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,子动画就需要单独指定所需的插值器或者使用默认值。

2.1TranslateAnimation平移动画




Android 动画基础知识学习_java

平移动画


可以简单实现抖动效果,转成gif掉帧有点严重,没有抖起来在res下创建anim文件夹,文件名translate_animation.xmlxml文件代码:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="false"
    android:duration="100"
    android:repeatMode="restart"
    >
    <translate
        android:repeatCount="2"
        android:fromXDelta="-10"
        android:fromYDelta="-5"
        android:toXDelta="10"
        android:toYDelta="5" />
</set>

java代码:

Button bt = (Button) view.findViewById(R.id.bt_translate_fragment_translate);
ImageView iv = (ImageView)view.findViewById(R.id.iv_translate_fragment_translate);
//初始化动画
Animation animation = AnimationUtils.loadAnimation(context, R.anim.translate_animation);
//点击按钮开始动画
bt.setOnClickListener((v) -> iv.startAnimation(animation));
Button bt = (Button) view.findViewById(R.id.bt_translate_fragment_translate);
ImageView iv = (ImageView)view.findViewById(R.id.iv_translate_fragment_translate);
//初始化动画
Animation animation = AnimationUtils.loadAnimation(context, R.anim.translate_animation);
//点击按钮开始动画
bt.setOnClickListener((v) -> iv.startAnimation(animation));

android:fromXDelta

  • x的起始坐标值,可以为数值、百分数、百分数

p

  • 。以

View

  • 的左上角为坐标系原点。负为左,正为右。



Android 动画基础知识学习_java_02

1.0 坐标图



  1. 数值: 10表示以当前View左上角坐标加10px为初始点
  2. 百分数: 50%表示以当前View的左上角加上当前View宽高的50%做为初始点
  3. 百分数

p

  1. : 50%p表示以当前View的左上角加上父控件宽高的50%做为初始点

android:toXDelta

  • x的结束坐标值

android:fromYDelta

  • y的起始坐标值。负为上,正为下

android:toYDelta

  • y的结束坐标值

需要注意的是,TranslateAnimation动画并不会改变View的位置布局属性。

例如,利用TranslateAnimation把一个Button改变了,点击移动后的Button是无效的,而点击Button移动前的原始空白位置会响应Button的点击事件。


2.2ScaleAnimation缩放动画




Android 动画基础知识学习_移动开发_03

缩放动画


xml代码:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fillAfter="false"
    android:repeatMode="reverse">
    <scale
        android:fromXScale="0.5"
        android:fromYScale="0.5"
        android:pivotX="-100"
        android:pivotY="-100"
        android:repeatCount="2"
        android:toXScale="1"
        android:toYScale="1" />
</set>

android:fromXScaleandroid:fromYScaleandroid:toXScaleandroid:toYScaleandroid:pivotX

  •  缩放的轴点的x轴的坐标。轴点为

View

  • 的左上角

android:pivotY

默认情况下轴点为View的中心点

感觉书上这里和我实际测试有些出入,我感觉默认是View的左上角。不晓得是我哪里搞错了,希望可以指出。感觉坐标系就是自己上面画的那个1.0坐标图


2.2RotateAnimation旋转动画




Android 动画基础知识学习_android_04

旋转动画


xml代码:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:repeatMode="reverse">
    <rotate
        android:fromDegrees="0"
        android:pivotX="235"
        android:pivotY="150"
        android:repeatCount="2"
        android:toDegrees="360" />
</set>

android:fromDegreesandroid:toDegreesandroid:pivotXandroid:pivotY


2.4AlphaAnimation透明度动画




Android 动画基础知识学习_android_05

透明度动画


xml代码:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="2000"
    android:repeatMode="reverse">
    <alpha
        android:fromAlpha="1.0"
        android:repeatCount="2"
        android:toAlpha="0.1" />
</set>

android:fromAlpha

  •  透明度的起始值,

1.0

  • 代表最不透明,值越小越透明

android:toAlpha


3. 帧动画

帧动画是顺序播放一组预先定义好的图片,类似播放电影。需要用到AnimationDrawable这个类。随便百度的吾王,一点没有表现出吾王美如画



Android 动画基础知识学习_android_06

帧动画



帧动画使用步骤:

  • 先在

drawable

  • 文件下,定义一个

animation-list

  • 文件,文件名字

frames_animation.xml

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@drawable/a"
        android:duration="500" />
    <item
        android:drawable="@drawable/b"
        android:duration="500" />
    <item
        android:drawable="@drawable/c"
        android:duration="500" />
</animation-list>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item
        android:drawable="@drawable/a"
        android:duration="500" />
    <item
        android:drawable="@drawable/b"
        android:duration="500" />
    <item
        android:drawable="@drawable/c"
        android:duration="500" />
</animation-list>

Drawable

  • 作为

View

  • 的背景播放
private void initView() {
    ImageView iv = (ImageView) findViewById(R.id.iv_frames_animation_activity);
    Button bt_start= (Button) findViewById(R.id.bt_start_frames_animation_activity);
    Button bt_stop= (Button) findViewById(R.id.bt_stop_frames_animation_activity);

    iv.setBackgroundResource(R.drawable.frames_animation);
    AnimationDrawable animation = (AnimationDrawable) iv.getBackground();

    bt_start.setOnClickListener((v)-> animation.start());
    bt_stop.setOnClickListener((v)->animation.stop());
}
private void initView() {
    ImageView iv = (ImageView) findViewById(R.id.iv_frames_animation_activity);
    Button bt_start= (Button) findViewById(R.id.bt_start_frames_animation_activity);
    Button bt_stop= (Button) findViewById(R.id.bt_stop_frames_animation_activity);

    iv.setBackgroundResource(R.drawable.frames_animation);
    AnimationDrawable animation = (AnimationDrawable) iv.getBackground();

    bt_start.setOnClickListener((v)-> animation.start());
    bt_stop.setOnClickListener((v)->animation.stop());
}

帧动画使用很简单,但很容易出现OOM。尽量避免使用较大较多的图片。


4.View动画的特殊使用场景

View动画除了四种基本使用场景外,还可以在ViewGroup中控制子元素的出场效果,在Activity中可以实现不同Activity之间的切换效果。

4.1 LayoutAnimation简单介绍

LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时,都会具有这种动画效果。这种效果常常用于ListView上。

挖坑:RecyclerViewitem的动画效果如何实现?



Android 动画基础知识学习_移动开发_07

LayoutAnimation动画



使用步骤:

1.指定子View的动画

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:repeatMode="reverse">
    <rotate
        android:fromDegrees="0"
        android:pivotX="235"
        android:pivotY="150"
        android:repeatCount="2"
        android:toDegrees="360" />
</set>

用的是2.2旋转的动画


2. 定义LayoutAnimation

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:animation="@anim/rotate_animation"
    android:animationOrder="reverse"
    android:delay="1" />
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:animation="@anim/rotate_animation"
    android:animationOrder="reverse"
    android:delay="1" />

android:animation

指定子元素入场显示的动画

android:animationOrder

子元素动画的顺序。有: nomal,reverse,random
nomal 顺序显示,排在前面的子元素先显示动画;
reverse 逆序显示,排在后面的子元素先显示动画;
random

android:delay

子元素开始动画的时间延迟。比如子元素的入场动画周期为300ms,0.5就表示每个子元素都需要延迟150ms。第一个进来延迟150ms,播放入场动画,第二个子元素延迟300ms播放入场动画。依次类推。


3.ViewGroup使用LayoutAniimation采用布局文件的形式,指定android:layoutAnimation

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:layoutAnimation="@anim/layout_anim"
    android:orientation="vertical"
    >

    <ImageView style="@style/img" />

    <ImageView style="@style/img" />

    <ImageView style="@style/img" />

</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:layoutAnimation="@anim/layout_anim"
    android:orientation="vertical"
    >

    <ImageView style="@style/img" />

    <ImageView style="@style/img" />

    <ImageView style="@style/img" />

</LinearLayout>

也可以通过代码来实现:

Animation animation= AnimationUtils.loadAnimation(context,R.anim.resId);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(1);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
目标ViewGroup.setLayoutAnimation(controller);
Animation animation= AnimationUtils.loadAnimation(context,R.anim.resId);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(1);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
目标ViewGroup.setLayoutAnimation(controller);

4.2Activity的切换效果

使用overidePendingTransition(int enterAnim, int exitAnim)可以改变Activity的的默认切换效果。这个方法 必须在startActivity()或者finish()之后才有效果。enterAnimActivity

  • 被打开时所需的动画资源

idexitAnimActivity

  • 被暂停时所需的动画资源

id启动一个Activity时:

Intent intent = new Intent(MainActivity.this, activity);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
Intent intent = new Intent(MainActivity.this, activity);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);

退出一个Activity时:

@Override
  public void finish() {
     super.finish();
     overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}
  @Override
  public void finish() {
     super.finish();
     overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}

1.属性动画

属性动画可以对任意对象的属性进行动画不仅仅是View,动画默认时间间隔是300ms,默认帧率是100ms/帧

作用:在一个时间间隔内完成对一个对象从属性值到另一个属性值的改变。

三个常用类:ValueAnimator,ObjectAnimator,AnimatorSet




Android 动画基础知识学习_xml_08

属性动画



Java代码

private void initView() {
        Button bt = (Button) findViewById(R.id.bt_object_animation_activity);
        ImageView iv = (ImageView) findViewById(R.id.iv_object_animation_activity);

        //改变背景属性
        ValueAnimator colorAnim = ObjectAnimator.ofInt(iv, "backgroundColor", Color.parseColor("#FF4081"), Color.CYAN);
        colorAnim.setRepeatCount(2);
        colorAnim.setRepeatMode(ObjectAnimator.REVERSE);
        colorAnim.setDuration(1000);
        colorAnim.setEvaluator(new ArgbEvaluator());//估值器

        //动画集合
        AnimatorSet set = new AnimatorSet();
        set.playTogether(
                ObjectAnimator.ofFloat(iv, "rotationX", 0, 360),//绕x轴旋转360度
                ObjectAnimator.ofFloat(iv, "rotation", 0, -90),//逆时针旋转90度
                ObjectAnimator.ofFloat(iv, "translationX", 0, 90),//右移
                ObjectAnimator.ofFloat(iv, "scaleY", 1, 0.5f),//y轴缩放到一半
                ObjectAnimator.ofFloat(iv, "alpha", 1, 0.25f, 1)//透明度变换
        );
        //延迟一秒开始
        set.setStartDelay(1000);

        bt.setOnClickListener((v) -> {
            //改变属性 位置  向下移动iv高的二分之一
            ObjectAnimator.ofFloat(iv, "translationY", iv.getHeight() / 2).start();
            //背景属性改变开始
            colorAnim.start();
            //集合动画
            set.setDuration(3000).start();
        });
}
private void initView() {
        Button bt = (Button) findViewById(R.id.bt_object_animation_activity);
        ImageView iv = (ImageView) findViewById(R.id.iv_object_animation_activity);

        //改变背景属性
        ValueAnimator colorAnim = ObjectAnimator.ofInt(iv, "backgroundColor", Color.parseColor("#FF4081"), Color.CYAN);
        colorAnim.setRepeatCount(2);
        colorAnim.setRepeatMode(ObjectAnimator.REVERSE);
        colorAnim.setDuration(1000);
        colorAnim.setEvaluator(new ArgbEvaluator());//估值器

        //动画集合
        AnimatorSet set = new AnimatorSet();
        set.playTogether(
                ObjectAnimator.ofFloat(iv, "rotationX", 0, 360),//绕x轴旋转360度
                ObjectAnimator.ofFloat(iv, "rotation", 0, -90),//逆时针旋转90度
                ObjectAnimator.ofFloat(iv, "translationX", 0, 90),//右移
                ObjectAnimator.ofFloat(iv, "scaleY", 1, 0.5f),//y轴缩放到一半
                ObjectAnimator.ofFloat(iv, "alpha", 1, 0.25f, 1)//透明度变换
        );
        //延迟一秒开始
        set.setStartDelay(1000);

        bt.setOnClickListener((v) -> {
            //改变属性 位置  向下移动iv高的二分之一
            ObjectAnimator.ofFloat(iv, "translationY", iv.getHeight() / 2).start();
            //背景属性改变开始
            colorAnim.start();
            //集合动画
            set.setDuration(3000).start();
        });
}

轴点默认为View的中心点。常用的propertyNamerotationXrotationYrotationtranslationXtranslationYscaleXscaleYalphawidthheight


1.2 常用方法介绍

Animation的api

1.2.1 ObjectAnimator类

ObjectAnimator.ofFloat(Object target, String propertyName, float... values)

Constructs and returns an ObjectAnimator that animates between float values.

  • 返回一个根据方法内的具体的

values

  • 创建的

ObjectAnimator

  • 对象

Object target

  1.  ,目标控件

View

  1. 的对象

String propertyName

  1. ,属性的名字

float... values


ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)

Constructs and returns an ObjectAnimator that animates between the sets of values specified in PropertyValueHolder objects

  • 返回一个由

PropertyValueHolder

  • 对象创建的

ObjectAnimator

  • 对象
public void byPropertyValuesHolder(ImageView iv) {
      PropertyValuesHolder pvh_1 = PropertyValuesHolder.ofFloat("rotationX", 0, 360);
      PropertyValuesHolder pvh_2 = PropertyValuesHolder.ofFloat("rotation", 0, -90);
      PropertyValuesHolder pvh_3 = PropertyValuesHolder.ofFloat("alpha", 1, 0.25f, 1);
      ObjectAnimator.ofPropertyValuesHolder(iv, pvh_1, pvh_2, pvh_3).setDuration(1000).start();
}
public void byPropertyValuesHolder(ImageView iv) {
      PropertyValuesHolder pvh_1 = PropertyValuesHolder.ofFloat("rotationX", 0, 360);
      PropertyValuesHolder pvh_2 = PropertyValuesHolder.ofFloat("rotation", 0, -90);
      PropertyValuesHolder pvh_3 = PropertyValuesHolder.ofFloat("alpha", 1, 0.25f, 1);
      ObjectAnimator.ofPropertyValuesHolder(iv, pvh_1, pvh_2, pvh_3).setDuration(1000).start();
}

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

Constructs and returns an ObjectAnimator that animates between int values.

  • 返回一个由

int

  • 值属性创建的

ObjectAnimator

  • 对象

setTarget(Object target)

  • 设置动画目标

View


1.2.2 ValueAnimator类

ValueAnimator

setEvaluator(new ArgbEvaluator())

  • 设置估值器

addUpdateListener(ValueAnimator.AnimatorUpdateListener listener)

Adds a listener to the set of listeners that are sent update events through the life of an animation.

  • 添加一个监听,可以用来在动画过程中改变属性

setRepeatCount(int num)

  • 设置动画的循环次数,默认为0,-1为无限循环

setStartDelay(long startDelay)

  • 设置动画开始的延迟时间

cancel()

  • 取消一个正在进行的动画。取消前,动画进行到哪个状态,取消后,就保持在那个状态。

end()

  • 结束动画。调用结束方法后,

View

  • 会跳转到结束状态。如果动画设置了循环次数

setRepeatCount()

  • 和重复模式

setRepeatMode(ObjectAnimator.REVERSE)

  • ,结束状态就要根据具体设置分析。

1.2.3 AnimatorSet类

playTogether(Animator... items)

Sets up this AnimatorSet to play all of the supplied animations at the same time.

  • 同时播放所有的

Animator

  • 动画对象。

playSequentially(Animator... items)

Sets up this AnimatorSet to play each of the supplied animations when the previous animation ends.

  • 顺序播放

Animator

  • 动画对象

setInterpolator(TimeInterpolator interpolator)

Sets the TimeInterpolator for all current child animations of this AnimatorSet.

  • 设置插值器

1.2.4 Animator类

直接子类:AnimatorSet 和 ValueAnimator

间接子类:ObjectAnimator 和 TimeAnimatorAnimator类的方法子类都可以直接使用。addListener(Animator.AnimatorListener listener)

Adds a listener to the set of listeners that are sent events through the life of an animation, such as start, repeat, and end.

  • 为动画添加一个监听过程的接口。如果不想实现

AnimatorListener

  • 接口中的所有方法也可以继承

AnimatorListenerAdapter

set.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animator) {
         toast("动画开始");
    }
    @Override
    public void onAnimationEnd(Animator animator) {
         toast("动画结束");
    }
    @Override
    public void onAnimationCancel(Animator animator) {
         toast("动画取消");
    }
    @Override
    public void onAnimationRepeat(Animator animator) {
        toast("动画重建");
    }
});
set.addListener(new Animator.AnimatorListener() {
    @Override
    public void onAnimationStart(Animator animator) {
         toast("动画开始");
    }
    @Override
    public void onAnimationEnd(Animator animator) {
         toast("动画结束");
    }
    @Override
    public void onAnimationCancel(Animator animator) {
         toast("动画取消");
    }
    @Override
    public void onAnimationRepeat(Animator animator) {
        toast("动画重建");
    }
});

实现了接口中全部的方法。


继承AnimatorListenerAdapter:

set.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        toast("动画结束");
    }
});
set.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        toast("动画结束");
    }
});

根据实际需求,实现不同的方法。


addPauseListener(Animator.AnimatorPauseListener listener)

Adds a pause listener to this animator

  • 为动画增加暂停监听

2.插值器和估值器

TimeInterpolator,时间插值器。用来根据时间流逝的百分比来计算出当前属性的值变化的百分比。

常用插值器:

名称

作用

LinearInterpolator

线性插值器。匀速动画

AccelerateDecelerateInterpolator

加速减速插值器

DecelerateInterpolator

匀减速插值器。动作越来越慢

BounceInterpolator

回弹插值器。到达平移后,回弹

CycleInterpolator

循环插值器。在两点间往还运动

PathInterpolator

路径插值器。根据单一方向定义的路径坐标运动

OvershootInterpolator

超越插值器。超出后,再返回来

AnticipateInterpolator

预期插值器。先反向运动再根据指定的方向运动

都是Interpolator的子类


TypeEvaluator,类型估值算法(估值器)。用来根据当前属性变化改变的百分比来计算改变后的属性值。IntEvaluator

  • ,针对整型属性

FloatEvaluator

  • ,针对浮点型属性

ArgbEvaluator

  • ,针对

Color

  • 属性

2.1简单Demo




Android 动画基础知识学习_移动开发_09

模拟小球下落



private void initView() {
    Button bt = (Button) findViewById(R.id.bt_interpolator_activity);
    ImageView iv = (ImageView) findViewById(R.id.iv_interpolator_activity);
    LinearLayout root = (LinearLayout) findViewById(R.id.root_interpolator_activity);//根布局

    AnimatorSet set = new AnimatorSet();
    set.setInterpolator(new BounceInterpolator());
    set.setDuration(3000);
    //利用View的post方法拿到根布局的高度
    root.post(() -> {
        //计算下降高度
        int height = root.getHeight() - iv.getHeight() - bt.getHeight();
        //设置动画
        set.play(ObjectAnimator.ofFloat(iv, "translationY", height));
    });

    bt.setOnClickListener(v ->set.start());
    }
private void initView() {
    Button bt = (Button) findViewById(R.id.bt_interpolator_activity);
    ImageView iv = (ImageView) findViewById(R.id.iv_interpolator_activity);
    LinearLayout root = (LinearLayout) findViewById(R.id.root_interpolator_activity);//根布局

    AnimatorSet set = new AnimatorSet();
    set.setInterpolator(new BounceInterpolator());
    set.setDuration(3000);
    //利用View的post方法拿到根布局的高度
    root.post(() -> {
        //计算下降高度
        int height = root.getHeight() - iv.getHeight() - bt.getHeight();
        //设置动画
        set.play(ObjectAnimator.ofFloat(iv, "translationY", height));
    });

    bt.setOnClickListener(v ->set.start());
    }

利用BounceInterpolator可以很方便的做出模拟小球下落的动画。也可以根据需求进行自定义插值器。


2.2 对任意属性做动画

Object的属性abc做动画,必须满足2个条件:object

  1. 必须提供

setAbc()

  1. 的方法。如果动画的时候没有传递初始值,还要提供

getAbc()

  1. 方法。因为系统要去

abc

  1. 的初始值。如果不满足,程序直接

Crashobject

setAbc

  1. 对属性

abc

  1. 所做的改变必须能够通过某种方法反映出来,比如

UI

  1. 改变之类的。这条不满足,动画无效但程序不会

Crash


2.2.1 改变Button的宽度

例如,想要利用属性对话来改变一个Button的宽度。

private void initView() {
    Button bt = (Button) findViewById(R.id.bt_button_activity);
    bt.setOnClickListener((v) -> performAnimate(bt));
}

private void performAnimate(Button bt) {
    ObjectAnimator.ofInt(bt, "width", 500).setDuration(1000).start();
}
private void initView() {
    Button bt = (Button) findViewById(R.id.bt_button_activity);
    bt.setOnClickListener((v) -> performAnimate(bt));
}

private void performAnimate(Button bt) {
    ObjectAnimator.ofInt(bt, "width", 500).setDuration(1000).start();
}

实际测试,这段代码完全不起作用。


2.2.2不起作用的原因

Button继承的TextView

/**
 * Makes the TextView exactly this many pixels wide.
 * You could do the same thing by specifying this number in the
 * LayoutParams.
 *
 * @see #setMaxWidth(int)
 * @see #setMinWidth(int)
 * @see #getMinWidth()
 * @see #getMaxWidth()
 *
 * @attr ref android.R.styleable#TextView_width
 */
 @android.view.RemotableViewMethod
 public void setWidth(int pixels) {
    mMaxWidth = mMinWidth = pixels;
    mMaxWidthMode = mMinWidthMode = PIXELS;

    requestLayout();
    invalidate();
 }


 /**
  * Return the width of the your view.
  *
  * @return The width of your view, in pixels.
  */
  @ViewDebug.ExportedProperty(category = "layout")
  public final int getWidth() {
     return mRight - mLeft;
  }
/**
 * Makes the TextView exactly this many pixels wide.
 * You could do the same thing by specifying this number in the
 * LayoutParams.
 *
 * @see #setMaxWidth(int)
 * @see #setMinWidth(int)
 * @see #getMinWidth()
 * @see #getMaxWidth()
 *
 * @attr ref android.R.styleable#TextView_width
 */
 @android.view.RemotableViewMethod
 public void setWidth(int pixels) {
    mMaxWidth = mMinWidth = pixels;
    mMaxWidthMode = mMinWidthMode = PIXELS;

    requestLayout();
    invalidate();
 }


 /**
  * Return the width of the your view.
  *
  * @return The width of your view, in pixels.
  */
  @ViewDebug.ExportedProperty(category = "layout")
  public final int getWidth() {
     return mRight - mLeft;
  }

getWidth()方法View的方法,是可以获取Button高度的。setWidth()方法TextView和子类的专属方法。是用来设置TextView的最大宽度和最小宽度的,并不是用来设置TextView的宽度的方法。TextView的宽度对应于XMLandroid:layout_widthsetWidth方法对应的是android:width。也就是说:ButtonsetWidth()getWidth()对应的就不是一个属性。只满足的条件1,不满足条件2


2.2.3 解决办法

有三种解决办法:

  • 如果有权限,给对象加上

get

set

  • 方法
  • 用一个类包装原始对象,间接提供

get

set

  • 方法
  • 采用

ValueAnimation

  • ,监听动画过程,实现属性的改变

getset方法往往拿不到权限。

利用包装类方法:

private void initView() {
    Button bt = (Button) findViewById(R.id.bt_button_activity);
    bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth()));
}

 private void performAnimate(Button bt,int width) {
    ButtonWrapper wrapper = new ButtonWrapper(bt);
    wrapper.setWidth(width);
    ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(1000).start();
}

private static class ButtonWrapper {
    private View target;

    public ButtonWrapper(View target) {
        this.target = target;
    }

    public int getWidth() {
        return target.getLayoutParams().width;
    }

    public void setWidth(int width) {
        target.getLayoutParams().width = width;
        target.requestLayout();
    }
}
private void initView() {
    Button bt = (Button) findViewById(R.id.bt_button_activity);
    bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth()));
}

 private void performAnimate(Button bt,int width) {
    ButtonWrapper wrapper = new ButtonWrapper(bt);
    wrapper.setWidth(width);
    ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(1000).start();
}

private static class ButtonWrapper {
    private View target;

    public ButtonWrapper(View target) {
        this.target = target;
    }

    public int getWidth() {
        return target.getLayoutParams().width;
    }

    public void setWidth(int width) {
        target.getLayoutParams().width = width;
        target.requestLayout();
    }
}

拿到Button的宽度后,设置给ButtonWrapper。这样动画开始后,Button会从原始大小开始变化。


利用ValueAnimation方法:

private void initView() {
    Button bt = (Button) findViewById(R.id.bt_button_activity);

    bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth(),500));
}

private void performAnimate(Button bt,int start, int end) {
    ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);

    //IntEvaluator对象,估值的时候使用
    IntEvaluator intEvaluator = new IntEvaluator();

    valueAnimator.addUpdateListener((animator -> {
        //获取当前动画的进度值 , 整型, 1到100
        int currentValue = (int) animator.getAnimatedValue();

        //获取当前进度的占整个动画过程的比例,浮点型, 0到1
        float fraction = animator.getAnimatedFraction();
        //直接利用整型估值器,通过比例计算宽度,然后Button设置
        bt.getLayoutParams().width = intEvaluator.evaluate(fraction,start,end);
        bt.requestLayout();
    }));
    //开启动画
    valueAnimator.setDuration(1000).start();
}
private void initView() {
    Button bt = (Button) findViewById(R.id.bt_button_activity);

    bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth(),500));
}

private void performAnimate(Button bt,int start, int end) {
    ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);

    //IntEvaluator对象,估值的时候使用
    IntEvaluator intEvaluator = new IntEvaluator();

    valueAnimator.addUpdateListener((animator -> {
        //获取当前动画的进度值 , 整型, 1到100
        int currentValue = (int) animator.getAnimatedValue();

        //获取当前进度的占整个动画过程的比例,浮点型, 0到1
        float fraction = animator.getAnimatedFraction();
        //直接利用整型估值器,通过比例计算宽度,然后Button设置
        bt.getLayoutParams().width = intEvaluator.evaluate(fraction,start,end);
        bt.requestLayout();
    }));
    //开启动画
    valueAnimator.setDuration(1000).start();
}

监控动画过程,利用IntEvaluator估值器


3.最后