Android 动画分析之属性动画
2017-03-21 23:13
141 查看
上一篇讲解了android中Tween动画的源码分析,今天接着讲android 3.0后出现的属性动画
Property动画的引入:
在3.0之前,android的提供的补间动画其实能满足大部分需求,比如平移,缩放等等,但后来一些不足,体现出来了,比如改变view的属性,又比如你改变Button位置,发现移动后Button并不能点击等。当然额外的做些处理,如在最终位置隐藏一个相同大小的控件,处理点击事件,给人假象,但补间动画的不足已经体现,因此,在3.0引入了属性动画。
Property动画分析:
我们先来看以下代码:
上述是属性动画的小demo,里面有三个重要的类:Animator,ObjectAnimator,AnimatorSet,还有最重要的类,比如ValueAnimator等,其中AnimatorSet是控制一组动画的执行。
我们来看看ObjectAnimator动画:
ObjectAnimator类:
ObjectAnimator继承自ValueAnimator,而ValueAnimator又继承的Animator类。ObjectAnimator提供了ofFloat,ofInt,ofObjectd等方法,这些方法设置动画的目标,属性以及开始,结束,及中间的任意属性值。以ofInt为例源码:
看注释,我们很容易理解这些参数的含义,第一个是动画作用的对象,第二个是对对象的哪个属性操作,第三个是动态参数,想完成什么怎样的动画。以开始代码为例:
以这句为例,在我代码里header_view是toolBar的view,属性是Y轴方向移动,最后参数是让view从当前Y值,移动到0的位置,也就是控件从当前位置,慢慢的向上移动直到消失的过程(稍后补动态图,勿喷)
我们是否发现,第一个参数,第三个参数都没问题,那第二个参数具体是啥呢。我们怎么知道translationY,translationX呢,这些怎么来的呢,那有人问,我没发现控件存在这些属性啊,何来属性动画一说,确实是,控件本身并没有这些属性,父类查看了也不存在,其实对于ObjectAnimator而言,它找的是这些属性的get,set方法,而不是直接属性,那我们看一下View源码,确实发现了set,get方法:
哇,是不是我们经常用的缩放,旋转,平移,透明度都在里面了。还是之前那句话,多看源码,多看源码,奥秘就在其中。
那有人问了,那我自定义的view的自定义属性可不可以作为第二个参数了,那当然可以了,当要保证自定义view里的这个属性对应有set和get方法。
2.ValueAnimator
上边我们已经看到了ObjectAnimator继承的是ValueAnimator,ValueAnimator算是属性动画中最核心的类,我们通常用的是ObjectAnimator。属性动画的机制是通过不断改变目标对象的属性值实现动画,其实我们已经看到了,在ofInt等方法里,并没有太多的逻辑处理,而从初始值到结束值之间的变化其实就是在ValueAnimator里实现的。
3.AnimatorSet
还是看最开始代码的例子,用到了AnimatorSet组合动画,因为我们单纯的一个动画无法满足效果,这时候就需要组合,比如上述代码的例子,点击屏幕中央,标题栏和底部导航栏都逐渐隐藏,就是组合动画的实现。
里面用到了play()方法,返回的是AnimatorSet.Builder。
继续研究Builder中有四个方法:分别是with,before,after。
上边四个重要方法,分别是:
after(Animator anim) :将现有动画插入到传入动画之后执行
after(long delay) 将现有动画延迟指定毫秒后执行
before(Animator anim) 将现有动画插入到传入的动画之前执行
with(Animator anim) 将现有动画和传入的动画同时执行
比如上边的动画如果不同时执行,比如底部导航栏隐藏后再隐藏标题栏可以这样:
4.ViewPropertyAnimator:
在android 3.1之后,android为大家提供了ViewPropertyAnimator类,其实我们发现上述使用是不是感觉有点繁琐,明明一个TextView设置内容:
textView.setText(..)这种语法格式是不是很简洁,一步了然。而ViewPropertyAnimator就是为了简化而出现的,比如设置TextView的缩放,可以使用
如果想移动textview的位置,比如10,10的坐标点,可以这样写:
是不是看上去简洁多了,其实很多语言,语法特点都有这方面的共性,比如拉姆达表达式,swift的语法,rxjava等等
需要注意的是我们这样执行,并不会再重新调用start()方法,因为新接口已经为我们封装了启动动画的功能。
小结:今天讲得是从源码分析的属性动画,因为有时候我们只关注使用,甚至不屑去深究下源码,今天就是借这篇博客,希望大家养成看源码的好习惯,例子不多,因为我们以后使用非常的频繁,等你熟练之后,代码也就那么回事,但如果文章能起到带大家阅读源码的习惯,也就算没白写。
Property动画的引入:
在3.0之前,android的提供的补间动画其实能满足大部分需求,比如平移,缩放等等,但后来一些不足,体现出来了,比如改变view的属性,又比如你改变Button位置,发现移动后Button并不能点击等。当然额外的做些处理,如在最终位置隐藏一个相同大小的控件,处理点击事件,给人假象,但补间动画的不足已经体现,因此,在3.0引入了属性动画。
Property动画分析:
我们先来看以下代码:
private void showAnimation(){ Animator ani = ObjectAnimator.ofFloat(header_view, "translationY", header_view.getTranslationY(), 0); ani.setDuration(500); ani.start(); }
private void showToolbar() { AnimatorSet as = new AnimatorSet(); Animator animator = ObjectAnimator.ofFloat(header_view, "translationY", header_view.getTranslationY(), 0); Animator animatorTools = ObjectAnimator.ofFloat(buttom_view, "translationY", buttom_view.getHeight(), 0); as.play(animator); as.play(animatorTools); as.start(); }
上述是属性动画的小demo,里面有三个重要的类:Animator,ObjectAnimator,AnimatorSet,还有最重要的类,比如ValueAnimator等,其中AnimatorSet是控制一组动画的执行。
我们来看看ObjectAnimator动画:
public final class ObjectAnimator extends ValueAnimator { private static final String LOG_TAG = "ObjectAnimator"; private static final boolean DBG = false; /** * A weak reference to the target object on which the property exists, set * in the constructor. We'll cancel the animation if this goes away. */ private WeakReference<Object> mTarget;
ObjectAnimator类:
ObjectAnimator继承自ValueAnimator,而ValueAnimator又继承的Animator类。ObjectAnimator提供了ofFloat,ofInt,ofObjectd等方法,这些方法设置动画的目标,属性以及开始,结束,及中间的任意属性值。以ofInt为例源码:
/** * Constructs and returns an ObjectAnimator that animates between int values. A single * value implies that that value is the one being animated to. Two values imply starting * and ending values. More than two values imply a starting value, values to animate through * along the way, and an ending value (these values will be distributed evenly across * the duration of the animation). * * @param target The object whose property is to be animated. This object should * have a public method on it called <code>setName()</code>, where <code>name</code> is * the value of the <code>propertyName</code> parameter. * @param propertyName The name of the property being animated. * @param values A set of values that the animation will animate between over time. * @return An ObjectAnimator object that is set up to animate between the given values. */ public static ObjectAnimator ofInt(Object target, String propertyName, int... values) { ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setIntValues(values); return anim; }
看注释,我们很容易理解这些参数的含义,第一个是动画作用的对象,第二个是对对象的哪个属性操作,第三个是动态参数,想完成什么怎样的动画。以开始代码为例:
Animator animator = ObjectAnimator.ofFloat(header_view, "translationY", header_view.getTranslationY(), 0);
以这句为例,在我代码里header_view是toolBar的view,属性是Y轴方向移动,最后参数是让view从当前Y值,移动到0的位置,也就是控件从当前位置,慢慢的向上移动直到消失的过程(稍后补动态图,勿喷)
我们是否发现,第一个参数,第三个参数都没问题,那第二个参数具体是啥呢。我们怎么知道translationY,translationX呢,这些怎么来的呢,那有人问,我没发现控件存在这些属性啊,何来属性动画一说,确实是,控件本身并没有这些属性,父类查看了也不存在,其实对于ObjectAnimator而言,它找的是这些属性的get,set方法,而不是直接属性,那我们看一下View源码,确实发现了set,get方法:
/** * Sets the horizontal location of this view relative to its {@link #getLeft() left} position. * This effectively positions the object post-layout, in addition to wherever the object's * layout placed it. * * @param translationX The horizontal position of this view relative to its left position, * in pixels. * * @attr ref android.R.styleable#View_translationX */ public void setTranslationX(float translationX) { if (translationX != getTranslationX()) { invalidateViewProperty(true, false); mRenderNode.setTranslationX(translationX); invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); notifySubtreeAccessibilityStateChangedIfNeeded(); } }那我们在看看View里还有哪些属性直接拿来用呢:
// drawing stream.addProperty("drawing:elevation", getElevation()); stream.addProperty("drawing:translationX", getTranslationX()); stream.addProperty("drawing:translationY", getTranslationY()); stream.addProperty("drawing:translationZ", getTranslationZ()); stream.addProperty("drawing:rotation", getRotation()); stream.addProperty("drawing:rotationX", getRotationX()); stream.addProperty("drawing:rotationY", getRotationY()); stream.addProperty("drawing:scaleX", getScaleX()); stream.addProperty("drawing:scaleY", getScaleY()); stream.addProperty("drawing:pivotX", getPivotX()); stream.addProperty("drawing:pivotY", getPivotY()); stream.addProperty("drawing:alpha", getAlpha()); stream.addProperty("drawing:transitionAlpha", getTransitionAlpha()); stream.addProperty("drawing:solidColor", getSolidColor());
哇,是不是我们经常用的缩放,旋转,平移,透明度都在里面了。还是之前那句话,多看源码,多看源码,奥秘就在其中。
那有人问了,那我自定义的view的自定义属性可不可以作为第二个参数了,那当然可以了,当要保证自定义view里的这个属性对应有set和get方法。
2.ValueAnimator
上边我们已经看到了ObjectAnimator继承的是ValueAnimator,ValueAnimator算是属性动画中最核心的类,我们通常用的是ObjectAnimator。属性动画的机制是通过不断改变目标对象的属性值实现动画,其实我们已经看到了,在ofInt等方法里,并没有太多的逻辑处理,而从初始值到结束值之间的变化其实就是在ValueAnimator里实现的。
3.AnimatorSet
还是看最开始代码的例子,用到了AnimatorSet组合动画,因为我们单纯的一个动画无法满足效果,这时候就需要组合,比如上述代码的例子,点击屏幕中央,标题栏和底部导航栏都逐渐隐藏,就是组合动画的实现。
Animator animator = ObjectAnimator.ofFloat(header_view, "translationY", header_view.getTranslationY(), 0); Animator animatorTools = ObjectAnimator.ofFloat(buttom_view, "translationY", buttom_view.getHeight(), 0); as.play(animator); as.play(animatorTools);
里面用到了play()方法,返回的是AnimatorSet.Builder。
public Builder play(Animator anim) { if (anim != null) { mNeedsSort = true; return new Builder(anim); } return null; }
继续研究Builder中有四个方法:分别是with,before,after。
public class Builder { /** * This tracks the current node being processed. It is supplied to the play() method * of AnimatorSet and passed into the constructor of Builder. */ private Node mCurrentNode; /** * package-private constructor. Builders are only constructed by AnimatorSet, when the * play() method is called. * * @param anim The animation that is the dependency for the other animations passed into * the other methods of this Builder object. */ Builder(Animator anim) { mCurrentNode = mNodeMap.get(anim); if (mCurrentNode == null) { mCurrentNode = new Node(anim); mNodeMap.put(anim, mCurrentNode); mNodes.add(mCurrentNode); } } /** * Sets up the given animation to play at the same time as the animation supplied in the * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object. * * @param anim The animation that will play when the animation supplied to the * {@link AnimatorSet#play(Animator)} method starts. */ public Builder with(Animator anim) { Node node = mNodeMap.get(anim); if (node == null) { node = new Node(anim); mNodeMap.put(anim, node); mNodes.add(node); } Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH); node.addDependency(dependency); return this; } /** * Sets up the given animation to play when the animation supplied in the * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object * ends. * * @param anim The animation that will play when the animation supplied to the * {@link AnimatorSet#play(Animator)} method ends. */ public Builder before(Animator anim) { mReversible = false; Node node = mNodeMap.get(anim); if (node == null) { node = new Node(anim); mNodeMap.put(anim, node); mNodes.add(node); } Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER); node.addDependency(dependency); return this; } /** * Sets up the given animation to play when the animation supplied in the * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object * to start when the animation supplied in this method call ends. * * @param anim The animation whose end will cause the animation supplied to the * {@link AnimatorSet#play(Animator)} method to play. */ public Builder after(Animator anim) { mReversible = false; Node node = mNodeMap.get(anim); if (node == null) { node = new Node(anim); mNodeMap.put(anim, node); mNodes.add(node); } Dependency dependency = new Dependency(node, Dependency.AFTER); mCurrentNode.addDependency(dependency); return this; } /** * Sets up the animation supplied in the * {@link AnimatorSet#play(Animator)} call that created this <code>Builder</code> object * to play when the given amount of time elapses. * * @param delay The number of milliseconds that should elapse before the * animation starts. */ public Builder after(long delay) { // setup dummy ValueAnimator just to run the clock ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); anim.setDuration(delay); after(anim); return this; } }
上边四个重要方法,分别是:
after(Animator anim) :将现有动画插入到传入动画之后执行
after(long delay) 将现有动画延迟指定毫秒后执行
before(Animator anim) 将现有动画插入到传入的动画之前执行
with(Animator anim) 将现有动画和传入的动画同时执行
比如上边的动画如果不同时执行,比如底部导航栏隐藏后再隐藏标题栏可以这样:
as.play(animator).after(animatorTools);可以链式语法执行的。
4.ViewPropertyAnimator:
在android 3.1之后,android为大家提供了ViewPropertyAnimator类,其实我们发现上述使用是不是感觉有点繁琐,明明一个TextView设置内容:
textView.setText(..)这种语法格式是不是很简洁,一步了然。而ViewPropertyAnimator就是为了简化而出现的,比如设置TextView的缩放,可以使用
tv.animate().scaleX(0.5f);
如果想移动textview的位置,比如10,10的坐标点,可以这样写:
tv.animate().x(10).y(10);
是不是看上去简洁多了,其实很多语言,语法特点都有这方面的共性,比如拉姆达表达式,swift的语法,rxjava等等
需要注意的是我们这样执行,并不会再重新调用start()方法,因为新接口已经为我们封装了启动动画的功能。
小结:今天讲得是从源码分析的属性动画,因为有时候我们只关注使用,甚至不屑去深究下源码,今天就是借这篇博客,希望大家养成看源码的好习惯,例子不多,因为我们以后使用非常的频繁,等你熟练之后,代码也就那么回事,但如果文章能起到带大家阅读源码的习惯,也就算没白写。
相关文章推荐
- Android属性动画深入分析:让你成为动画牛人
- Android属性动画深入分析:让你成为动画牛人
- Android属性动画深入分析:让你成为动画牛人
- Android属性动画深入分析:让你成为动画牛人
- 动画三 Android属性动画深入分析:让你成为动画牛人
- Android源码分析—属性动画的工作原理
- Android属性动画深入分析:让你成为动画牛人
- Android属性动画-Property Animation(一) 原理分析
- Android属性动画深入分析:让你成为动画牛人
- Android源码分析—属性动画的工作原理
- Android属性动画深入分析:让你成为动画牛人
- Android属性动画深入分析
- Android属性动画深入分析:让你成为动画牛人
- Android属性动画深入分析:让你成为动画牛人
- Android源码分析—属性动画的工作原理
- Android属性动画深入分析
- Android属性动画深入分析:让你成为动画牛人
- Android源码分析—属性动画的工作原理
- 动画四 Android源码分析—属性动画的工作原理
- Android属性动画分析