OliverLau 2019-06-28
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位大神表示感谢,膜拜!!!
上一篇文章呢,我们说了关于View动画的那些事,这里也在总结一下,使用View动画时需要注意以下4点:(更多详情请参看我的上一篇博客。)
那么本章呢是来介绍Android动画的另外一个大类属性动画
属性动画是API11新加入的特性,和View动画不同,它可以对任何对象做动画,甚至还可以没有对象,动画默认时间间隔300ms,默认帧率10ms/帧。其可以达到的效果是:在一个时间间隔内完成对对象从一个属性值到另一个属性值得改变。常用属性动画类ValueAnimator、ObjectAnimator和AnimationSet,其中ObjectAnimator继承于ValueAnimator.
注:gihub上JakeWharton大神对API11之前做了属性动画的兼容,它的原理其实也很简单,主要就是判断当前sdk版本,如果大于API11,那么就调用官方的API,否则自己实现动画效果。另外,在API使用方面,它与官方的属性动画基本一致。另外,在API使用方面,它与官方的属性动画基本一致。比如ObjectAnimator、ValueAnimator等等。 感兴趣的同学可以访问该项目的github网址NineOldAndroids
Java代码实现
例如改变一个对象(obj)的translationY属性,可以写为
ValueAnimator.ofFloat(obj,"translationY",100);
XML实现(属性动画的XML描述语法的固定格式)
<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering=["sequentially"|"together"]> <objectAnimator android:propertyName="string" android:duration="int" android:valueFrom="float|int|color" android:valueTo="float|int|color" android:startOffset="int" android:repeatCount="int" android:repeatMode=["restart"|"reverse"] android:valueType=["colorType"|"intType"]> </objectAnimator> <animator android:duration="int" android:valueFrom="float|int|color" android:valueTo="float|int|color" android:startOffset="int" android:repeatCount="int" android:repeatMode=["restart"|"reverse"] android:valueType=["colorType"|"intType"]> </animator> ... </set>
很多属性都是见名知义的,下面呢简单介绍一下各属性名称的含义。
属性动画的核心类有3个类,AnimatorSet,ObjectAnimator以及ValueAnimator
android:duration:表示动画的时长
android:valueFrom:表示属性的起始值
android:valueTo:表示属性的结束值
android:startOffset:表示动画的延迟时间,动画开始后,需要延迟多少毫秒后才会真正播放该动画
android:repeatCount:表示动画的重复次数,默认值是0,为-1时,表示无限循环。
android:repeatMode:表示动画的重复播放模式,restart表示动画每次都是重新开始播放,reverse表示动画第1 次播放完毕后,第2次会逆向播放,第3次又从头开始播放,以此类推
android:propertyName:表示属性动画作用对象的属性名称
android:valueType:表示android:propertyName的值的类型,分为intType,和floatType,分别代表整型数值和浮点型数值,若android:propertyName指定的属性表示的是颜色,那么无需指定android:valueType,系统会自动适配
其他属性的含义与上面的<animatior>一致。
我们先来看一个需求:要求对一个Button做动画,要求让其宽度从原始宽度增加到500px。这也太简单了,
Button mButton = (Button) findViewById(R.id.button); ObjectAnimator.ofInt(mButton,"width",500).setDuration(1000).start();
程序运行,但是却没有效果,这是为什么呢,仔细想想没效果也是应该的,因为你随便传递了一个属性名称过去,轻则动画没有效果,重则直接Crash。那么这个号称可以对任意属性做动画的属性动画使用的时候有哪些需要注意的地方呢
以上的条件缺一不可
这时又有一个问题如果想要对一个对象的属性做动画,但是属性又没有对应的get和set方法怎么办呢??
概括来讲有如下3种解决办法:
下面仍以上面Button的宽度动画作为需求给出方法2,3的解决代码
方法2:
mButton = (Button) findViewById(R.id.button); ObjectAnimator.ofInt(new ViewWrapper(mButton),"width",500).setDuration(1000).start(); private class ViewWrapper{ private View mTarget; public ViewWrapper(View mTarget) { this.mTarget = mTarget; } public int getWidth() { return mTarget.getLayoutParams().width; } public void setWidth(int width) { mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } }
方法3:
使用方法3之前,我们先来看属性动画的监听器AnimatorUpdateListener和AnimatorListener
public static interface AnimatorListener { void onAnimationStart(Animator animation); void onAnimationEnd(Animator animation); void onAnimationCancel(Animator animation); void onAnimationRepeat(Animator animation); }
如上代码所示AnimatorListener监听了动画的开始、结束、取消和重复播放,同时系统提供了AnimatorListenerAdapter适配器方便我们使用,我们可以继承这个类并有选择的实现方法。
public static interface AnimatorUpdateListener { /** * <p>Notifies the occurrence of another frame of the animation.</p> * * @param animation The animation which was repeated. */ void onAnimationUpdate(ValueAnimator animation); }
如上图所示,AnimatorUpdateListener 监听了动画的整个过程,动画每播放一帧,onAnimationUpdate就被调用一次,
下面就来看一下如何使用上面的属性动画的监听器来实现属性动画
mButton = (Button) findViewById(R.id.button); performAnimate(mButton, mButton.getWidth(), 500); private void performAnimate(final View target, final int start, final int end) { ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { // 持有一个IntEvaluator对象,方便下面估值的时候使用 private IntEvaluator mEvaluator = new IntEvaluator(); @Override public void onAnimationUpdate(ValueAnimator animator) { // 获得当前动画的进度值,整型,1-100之间 int currentValue = (Integer) animator.getAnimatedValue(); Log.d(TAG, "current value: " + currentValue); // 获得当前进度占整个动画过程的比例,浮点型,0-1之间 float fraction = animator.getAnimatedFraction(); // 直接调用整型估值器通过比例计算出宽度,然后再设给Button target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end); target.requestLayout(); } }); valueAnimator.setDuration(5000).start(); }
本章呢接着上一篇说了Android动画的另外一个大类属性动画,至此Android动画相关的文章完结,因笔者水平有限,所以有不当之处还请指出
此致,敬礼