Android动画之属性动画

zhuoqi 3年前
   <h3><strong>概述</strong></h3>    <p>Android属性动画是Android 3.0之后添加的一个非常强大的动画。与视图动画不同的地方在于,属性动画能够真正的改变View的属性。例如:如果我们通过视图动画移动了一个View,那么这个View真正的位置并没有发生改变,它的点击事件依然保留在原来的位置。而如果我们通过属性动画改变了一个View的位置,那么它真实的位置也就改变了。此外属性动画并不局限于对View实现动画,它几乎可以对任何一个对象的任何属性实现动画。</p>    <h3><strong>Animator三个子类</strong></h3>    <p>通常我们实现属性动画并不需要直接使用 Animator ,而是使用它的以下三个子类:</p>    <ul>     <li> <p>ObjectAnimator</p> </li>     <li> <p>ValueAnimator</p> </li>     <li> <p>AnimatorSet</p> </li>    </ul>    <p><strong>ObjectAnimator</strong></p>    <p>一般我通过ObjectAnimator的工厂方法 ofFloat() 来获得它的实例(其他的方法还有 ofInt() 和 ofArgb() )。</p>    <p>例如下代码实现了一个位移动画:</p>    <pre>  <code class="language-java">ObjectAnimator translate = ObjectAnimator.ofFloat(view, "translationX", 200f);  translate.setDuration(1000);    translate.start();</code></pre>    <p><strong>ofFloat() 参数说明</strong></p>    <ol>     <li> <p>第一参数是一个需要实现动画的View。</p> </li>     <li> <p>第二个参数是需要操作的属性。</p> </li>     <li> <p>最后一个其实是可变参数。用来表示属性值的一系列取值。当只传一个值的时候,表示从初始值,变化到该值。</p> </li>    </ol>    <p><strong>常用属性值</strong></p>    <ul>     <li> <p>translationX 和 translationY : 控制View距离左边和顶部的距离的增加值。是一个相对值。</p> </li>     <li> <p>rotation 、 rotationX 和 rotationY : rotation 是控制View围绕其支点进行旋转。 rotationX 和 rotationY 分别是围绕X轴和Y轴旋转。</p> </li>     <li> <p>scaleX 和 scaleY : 控制View的缩放。</p> </li>     <li> <p>pivotX 和 pivotY : 控制View的支点位置,进行旋转和缩放,默认是View的中点。它们都是 float 值, 0 表示View的最左边和最顶端, 1 表示最右端和最下端。</p> </li>     <li> <p>alpha : 控制View的透明度。</p> </li>     <li> <p>x 和 y : 控制View在布局容器中距离左边和顶部的距离。是一个绝对值。</p> </li>    </ul>    <p><strong>注意</strong></p>    <p>当用属性动画操作一个View的属性时,这个View必须具有相应的get和set方法。</p>    <p><strong>ValueAnimator</strong></p>    <p>ValueAnimator 是 ObjectAnimator 的父类。它有点像一个数值发生器。不提供任何动画效果。通常我们需要在 onAnimationUpdate 获取当前时间点发生的值,然后操作对象的属性,以实现动画效果。</p>    <p>如下代码实现了一个倒计时效果:</p>    <pre>  <code class="language-java">ValueAnimator countDown = ValueAnimator.ofInt(10, 0);  countDown.setDuration(10000);  countDown.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {      textView.setText("" + animation.getAnimatedValue());      }  });  countDown.start();</code></pre>    <p>补充</p>    <p>系统大约每10ms刷新一次,来更新属性值。所以如果我们要做一个以秒为单位的计时器,可以直接用这种效果实现。</p>    <p><strong>AnimatorSet</strong></p>    <p>如果我们要对一个对象的多个属性实现动画,通常有如下两种做法:</p>    <ul>     <li> <p>使用PropertyValuesHolder</p> </li>     <li> <p>使用AnimatorSet</p> </li>    </ul>    <p><strong>使用PropertyValuesHolder</strong></p>    <pre>  <code class="language-java">PropertyValuesHolder rotate = PropertyValuesHolder.ofFloat("rotation", 0f, 360f);  PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 2.0f);  PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 2.0f);  ObjectAnimator together = ObjectAnimator.ofPropertyValuesHolder(view, rotate, scaleX, scaleY);  together.setDuration(1000);  together.start();</code></pre>    <p>使用AnimatorSet</p>    <pre>  <code class="language-java">ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f).setDuration(1000);  ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 2.0f).setDuration(1000);  ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f, 2.0f).setDuration(1000);  AnimatorSet set = new AnimatorSet();    set.playSequentially(rotate, scaleX, scaleY);  set.setDuration(1000);  set.start();</code></pre>    <p>补充</p>    <p>AnimatorSet可以通过playSequentially(),playTogether(),来控制动画的串行和并行。通过set.play().with()、before()、after()等方法可以实现动画的多种协同效果。</p>    <h3><strong>操作一个没有set和get的属性</strong></h3>    <p>上文已经提到当用属性动画操作一个View的属性时,这个View必须具有相应的get和set方法。那么如果我们想操作一个没有get和set的属性,该怎么办呢?</p>    <p>通常有三种实现方式:</p>    <ul>     <li> <p>自己创建一个属性类。</p> </li>     <li> <p>通过一个包装类来实现。</p> </li>     <li> <p>通过ValueAnimator来实现。</p> </li>    </ul>    <p>通过包装类实现</p>    <p>当我们查看View的set和get方法时,它只有scaleX和scaleY,而没有scale属性。有时候,我们需要对一个View的X和Y都进行缩放,又不想使用两次ObjectAnimator,就可以通过如下方法实现。</p>    <p>如下代码是一个包装类的示例:</p>    <pre>  <code class="language-java">public class WrapperView {      private View target;      private float scale;      public WrapperView(View target){          this.target = target;      }      public setScale(float scale){          this.scale = scale;          target.setScaleX = scale;          target.setScaleY = scale;      }      public float getScale(){          return scale;      }  }</code></pre>    <p>使用该包装类直接实现对View的缩放:</p>    <pre>  <code class="language-java">WrapperView target = new WrapperView(view);  ObjectAnimator scale = ObjectAnimator.ofFloat(target,"scale",1f,2f);  scale.setDuration(1000);    scale.start();</code></pre>    <p><strong>通过ValueAnimator实现</strong></p>    <p>如果我们想实现一个View在一个曲线运动的动画,就可以通过如下方法实现。</p>    <p>(曲线公式为:y=-1/150 <em>x^2+4</em> x )</p>    <pre>  <code class="language-java">public static final int TRANS_X = 600;  ValueAnimator animator = new ValueAnimator();  float offsetX = view.getX();  float offsetY = view.getY();  animator.setDuration(2000);  // 和ofFloat类似,设置属性的起始值和结束值。  animator.setObjectValues(new PointF(offsetX, offsetY), new PointF(TRANS_X + offsetX, offsetY));  // 设置自定义的Evaluator.  animator.setEvaluator(new TypeEvaluator<PointF>() {      @Override      // api level 21以上已经实现了PointFEvalutor.      public PointF evaluate(float fraction, PointF startValue, PointF endValue) {          PointF pointF = new PointF();          float d = fraction * TRANS_X;          pointF.x = startValue.x + d;          pointF.y = startValue.y + (1.0f / 150f) * d * d - 4 * d;          return pointF;      }  });  // 通过监听器得到相应的Evaluator值,并应用。  animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {          PointF pointF = (PointF) animation.getAnimatedValue();          view.setX(pointF.x);          view.setY(pointF.y);      }  });  animator.start();</code></pre>    <p><strong>说明</strong></p>    <p>通常如果我们需要操作一个不存在的属性,我们需要自定义一个 Evaluator 。通过这个 Evaluator 在动画运行的过程中生成相应的值。然后给 ValueAnimator 设置一个监听器,通过 getAnimatedValue() 来得到相应的值。然后根据这个值,做相应的操作。</p>    <p><strong>关于Evaluator</strong></p>    <p>Evaluator 翻译过来是估值器的意思,实际上它是一种计算属性值的算法。</p>    <ul>     <li>系统定义的 Evaluator 有ArgbEvaluator, FloatEvaluator, IntEvaluator, PointFEvaluator等。</li>     <li>我们自定义 Evaluator 只需要实现 TypeEvaluator 接口,并重写 T evaluate(float fraction,T startValue,T endValue) 方法即可。其中 fraction 是根据设置的 Interpolator 生成的(系统默认的Interpolator是 AccelerateDecelerateInterpolator ,即先加速后减速。),它表示变化的百分比(0表示0%,1表示100%)。例如:如果我们将一个View从(0,0)移动到(0,100), duration 是1000ms,Interpolator为 LinearInterpolator 。那么在动画执行到500ms时,这个 fraction 就等于0.5。</li>    </ul>    <h3><strong>在xml中使用属性动画</strong></h3>    <p>属性动画的保存在res/animator文件夹下:</p>    <p>如果代码定义了一个AnimatorSet:</p>    <p>xml文件</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <set xmlns:android="http://schemas.android.com/apk/res/android"       android:ordering="sequentially">      <objectAnimator          android:duration="2000"          android:propertyName="translationY"          android:valueTo="-200f"          android:valueType="floatType"/>        <set android:ordering="together">          <objectAnimator              android:duration="2000"              android:propertyName="scaleX"              android:valueTo="2.0f"              android:valueType="floatType"/>          <objectAnimator              android:duration="2000"              android:propertyName="scaleY"              android:valueTo="2.0f"              android:valueType="floatType"/>      </set>  </set></code></pre>    <p>在Java代码中调用</p>    <pre>  <code class="language-java">Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator);  animator.setTarget(view);  animator.start();</code></pre>    <h3><strong>通过KeyFrame定义动画</strong></h3>    <pre>  <code class="language-java">// 这里ofFloat(),的第一个参数表示动画完成的百分比。  Keyframe keyframe1 = Keyframe.ofFloat(0f, 0f);  Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 360f);  Keyframe keyframe3 = Keyframe.ofFloat(1.0f, 0f);  PropertyValuesHolder pvh = PropertyValuesHolder.ofKeyframe("rotation", keyframe1, keyframe2, keyframe3);  ObjectAnimator rotate = ObjectAnimator.ofPropertyValuesHolder(view, pvh);  rotate.setDuration(2000);  rotate.start();</code></pre>    <h3><strong>动画事件的监听</strong></h3>    <p>我们可以使用 AnimatorListen 接口来监听动画的Start、Repeat、End、Cancel。但是这样做会过于繁琐,有时候我们只想监听动画的部分事件,这时候我们可以使用 AnimatorListenAdapter 接口。</p>    <h3><strong>直接使用View的animate方法</strong></h3>    <pre>  <code class="language-java">view.animate()      .alpha(0)      .y(300)      .setDuration(1000)      .withStartAction(new Runnable(){          @Override          public void run(){          }      })      .withEndAction(new Runnable(){          @Override          public void run(){              runOnUiThread(new Runnable(){              @Override              public void run(){              }              });          }      }).start();</code></pre>    <p>注: 本文主要参考有:网易微专业课程、Android群英传。当然还有Api guide。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/a151bc9bf12e</p>    <p> </p>