Android 动画基础知识学习(下)
学习资料:Android开发艺术探索和Animation的api
1.属性动画
属性动画可以对任意对象的属性进行动画不仅仅是View,动画默认时间间隔是300ms,默认帧率是100ms/帧。
作用:在一个时间间隔内完成对一个对象从属性值到另一个属性值的改变。
三个常用类:ValueAnimator,ObjectAnimator,AnimatorSet

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();
});
}
轴点默认为View的中心点。
常用的propertyName:
rotationX围绕x轴旋转rotationY围绕y轴旋转rotation围绕轴点旋转translationX在x轴方向上平移translationY在y轴方向上平移scaleX在x轴方向缩放scaleY在y轴方向缩放alpha透明度width宽度height高度
1.2 常用方法介绍
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,目标控件View的对象String propertyName,属性的名字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(); }
-
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 和 TimeAnimator
Animator类的方法子类都可以直接使用。
-
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("动画重建");
}
});
实现了接口中全部的方法。
继承AnimatorListenerAdapter:
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

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必须提供setAbc()的方法。如果动画的时候没有传递初始值,还要提供getAbc()方法。因为系统要去abc的初始值。如果不满足,程序直接Crashobject的setAbc对属性abc所做的改变必须能够通过某种方法反映出来,比如UI改变之类的。这条不满足,动画无效但程序不会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();
}
实际测试,这段代码完全不起作用。
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;
}
getWidth()方法View的方法,是可以获取Button高度的。setWidth()方法TextView和子类的专属方法。是用来设置TextView的最大宽度和最小宽度的,并不是用来设置TextView的宽度的方法。TextView的宽度对应于XML的android:layout_width,setWidth方法对应的是android:width。
也就是说:Button的setWidth()和getWidth()对应的就不是一个属性。只满足的条件1,不满足条件2
2.2.3 解决办法
有三种解决办法:
- 如果有权限,给对象加上
get和set方法 - 用一个类包装原始对象,间接提供
get和set方法 - 采用
ValueAnimation,监听动画过程,实现属性的改变
get和set方法往往拿不到权限。
利用包装类方法:
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();
}
监控动画过程,利用IntEvaluator估值器
3.最后
动画基础知识大概介绍完,自定义TypeEvaluator和Interpolator以及配合自定义View更多高级的用法,以后再做补充。
接下来相当长的时间会用来学习自定义View。想通过一系列博客来记录学习,一开始先学习一些View的基础属性知识为学习自定义View做准备,再学习具体的测量,绘制过程,View的事件体系,工作原理。学习过程中间会加入继续对动画的深入学习,也可能会加入RxJava的后续学习或者其他的框架的学习。
原文链接:http://www.jianshu.com/p/6a53ce436fd9
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

浙公网安备 33010602011771号