Android View动画和属性动画

gucheng_bw 8年前
   <p>在应用中, 动画效果提升用户体验, 主要分为 <strong>View动画</strong> 和 <strong>属性动画</strong> . View动画变换场景图片效果, 效果包括平移(translate), 缩放(scale), 旋转(rotate), 透明(alpha); 属性动画动态地改变改变属性, 达到动画效果. </p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0fd471bc3d52efcad1dfbe16852ccea0.png"></p>    <p style="text-align:center">Animation</p>    <h2>View动画</h2>    <p>动画包含四种平移, 缩放, 旋转, 透明, 也支持组合使用.</p>    <pre>  <code class="language-java">mAnimations = new ArrayList<>();  mAnimations.add(AnimationUtils.loadAnimation(context, R.anim.anim_translate));  mAnimations.add(AnimationUtils.loadAnimation(context, R.anim.anim_scale));  mAnimations.add(AnimationUtils.loadAnimation(context, R.anim.anim_rotate));  mAnimations.add(AnimationUtils.loadAnimation(context, R.anim.anim_alpha));  mAnimations.add(AnimationUtils.loadAnimation(context, R.anim.anim_all));</code></pre>    <p>平移动画: duration 持续时间; fromXDelta 起始X坐标, fromYDelta 起始Y坐标; toXDelta 终止X坐标, toYDelta 终止Y坐标.</p>    <pre>  <code class="language-java"><set xmlns:android="http://schemas.android.com/apk/res/android"       android:fillAfter="true"       android:interpolator="@android:anim/accelerate_interpolator">      <!--平移动画-->      <translate          android:duration="2000"          android:fromXDelta="50"          android:fromYDelta="-100"          android:toXDelta="0"          android:toYDelta="0"/>  </set></code></pre>    <p>fillAfter 动画完成后停留, 即在平移后不复原; interpolator 变换插值器.</p>    <p>缩放动画: fromXScale 起始宽度比例, fromYScale 起始高度比例; toXScale 终止宽度比例, toYScale 终止高度比例.</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <set xmlns:android="http://schemas.android.com/apk/res/android">      <!--缩放动画-->      <scale          android:duration="2000"          android:fromXScale="0.0"          android:fromYScale="0.0"          android:toXScale="1.0"          android:toYScale="1.0"/>  </set></code></pre>    <p>旋转动画: fromDegrees 起始角度, toDegrees 终止角度; pivotX 旋转中心X坐标, pivotY 旋转中心Y坐标.</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <set xmlns:android="http://schemas.android.com/apk/res/android"       android:fillAfter="true">      <!--旋转动画-->      <rotate          android:duration="2000"          android:fromDegrees="0"          android:pivotX="50%"          android:pivotY="50%"          android:toDegrees="-720"/>  </set></code></pre>    <p>透明动画: fromAlpha 起始透明度, toAlpha 终止透明度.</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <set xmlns:android="http://schemas.android.com/apk/res/android">      <!--透明动画-->      <alpha          android:duration="2000"          android:fromAlpha="0.1"          android:toAlpha="1.0"/>  </set></code></pre>    <p>组合动画: 融合平移, 缩放, 旋转, 透明四种动画, 效果是图片旋转着从左上角滚入屏幕, 逐渐变大变清晰.</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <set xmlns:android="http://schemas.android.com/apk/res/android"       android:duration="2000">      <!--平移动画-->      <translate          android:fromXDelta="50"          android:fromYDelta="-100"          android:toXDelta="0"          android:toYDelta="0"/>        <!--缩放动画-->      <scale          android:duration="2000"          android:fromXScale="0.0"          android:fromYScale="0.0"          android:toXScale="1.0"          android:toYScale="1.0"/>        <!--旋转动画-->      <rotate          android:duration="2000"          android:fromDegrees="0"          android:pivotX="50%"          android:pivotY="50%"          android:toDegrees="-720"/>        <!--透明动画-->      <alpha          android:duration="2000"          android:fromAlpha="0.1"          android:toAlpha="1.0"/>  </set></code></pre>    <p>帧动画: 特殊动画, 不断变换图片, 模拟动画效果. animation-list 动画列表, 每个 item 表示一个图片, duration 是图片停留时间. oneshot 值是true持续一次, false不断循环.</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <animation-list      xmlns:android="http://schemas.android.com/apk/res/android"      android:oneshot="false">      <!--循环显示三个图片-->      <item          android:drawable="@drawable/seo_square"          android:duration="250"/>      <item          android:drawable="@drawable/kim_square"          android:duration="250"/>      <item          android:drawable="@drawable/sunny_square"          android:duration="250"/>  </animation-list></code></pre>    <p>自定义动画: 重载 initialize 和 applyTransformation 方法. initialize 初始化动画; applyTransformation 应用转换, 参数 interpolatedTime 表示差值次数.</p>    <pre>  <code class="language-java">public class Rotate3dAnimation extends Animation {      private final float mFromDegrees;      private final float mToDegrees;      private final float mCenterX;      private final float mCenterY;      private final float mDepthZ;      private final boolean mReverse;      private Camera mCamera;        public Rotate3dAnimation(              float fromDegrees, float toDegrees,              float centerX, float centerY,              float depthZ, boolean reverse) {          mFromDegrees = fromDegrees;          mToDegrees = toDegrees;          mCenterX = centerX;          mCenterY = centerY;          mDepthZ = depthZ;          mReverse = reverse;      }        @Override public void initialize(int width, int height, int parentWidth, int parentHeight) {          super.initialize(width, height, parentWidth, parentHeight);          mCamera = new Camera();      }        @Override protected void applyTransformation(float interpolatedTime, Transformation t) {          final float fromDegrees = mFromDegrees;          float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime); // 结尾度数            // 中心点          final float centerX = mCenterX;          final float centerY = mCenterY;            final Camera camera = mCamera;          final Matrix matrix = t.getMatrix();            camera.save(); // 照相机            // Z轴平移          if (mReverse) {              camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);          } else {              camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));          }            camera.rotateY(degrees); // Y轴旋转          camera.getMatrix(matrix);          camera.restore();            // View的中心点进行旋转          matrix.preTranslate(-centerX, -centerY);          matrix.postTranslate(centerX, centerX);            super.applyTransformation(interpolatedTime, t);      }  }</code></pre>    <p>RecyclerList的项也支持动画式插入.</p>    <pre>  <code class="language-java">@Override public void onBindViewHolder(final GridViewHolder holder, final int position) {      // ...      setAnimation(holder.getContainer(), position);  }  private void setAnimation(View viewToAnimate, int position) {      if (position > mLastPosition || mLastPosition == -1) {          Animation animation = AnimationUtils.loadAnimation(mContext, android.R.anim.slide_in_left);          viewToAnimate.startAnimation(animation);          mLastPosition = position;      }  }</code></pre>    <h2>属性动画</h2>    <p>属性动画通过变换对象属性, 实现动画效果, 对应属性必须含有set和get方法, 支持调用. 对于自定义的属性, 则使用 <strong>Wrapper</strong> 或 <strong>插值方式</strong> .</p>    <p>属性动画仅支持 <strong>API 11</strong> 以上版本, 以前版本使用 支持库 .</p>    <p>Wrapper: 根据View的变换属性, 提供 <strong>宽度(width)</strong> 的设置(set)与获取(get).</p>    <pre>  <code class="language-java">private void performWrapperAnimation(final View view, final int start, final int end) {      ViewWrapper vw = new ViewWrapper(view);      ObjectAnimator.ofInt(vw, "width", start, end).setDuration(2000).start(); // 启动动画  }    // 视图包装, 提供Width的get和set方法  private static class ViewWrapper {      private View mView;        public ViewWrapper(View view) {          mView = view;      }        @SuppressWarnings("unused")      public int getWidth() {          return mView.getLayoutParams().width;      }        @SuppressWarnings("unused")      public void setWidth(int width) {          mView.getLayoutParams().width = width;          mView.requestLayout();      }  }</code></pre>    <p>requestLayout: 当View确定自身不再适合现有区域时, 调用requestLayout, 要求Parent View重新调用onMeasure和onLayout重新设置当前View的位置.</p>    <p>特别当View的LayoutParams发生改变时, 并且值还未应用至View上, 这时候适合调用此方法.</p>    <p>invalidate: View本身调用迫使View重绘.</p>    <p>差值: 使用 <strong>ValueAnimator</strong> (属性动画), 并设置更新, 添加 <strong>IntEvaluator</strong> (整数估值器), 渐进地设置View的宽度.</p>    <pre>  <code class="language-java">private void performListenerAnimation(final View view, 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();                // 获得当前进度占整个动画过程的比例,浮点型,0-1之间              float fraction = animator.getAnimatedFraction();              // 直接调用整型估值器通过比例计算出宽度,然后再设给Button              view.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);              view.requestLayout();          }  ![  ![  ![ezgif.com-b9f6ca81b1.gif](http://upload-images.jianshu.io/upload_images/749674-2b7e12f82fdcae62.gif?imageMogr2/auto-orient/strip)  ](http://upload-images.jianshu.io/upload_images/749674-cdf2dd08094b34fa.gif?imageMogr2/auto-orient/strip)  ](http://upload-images.jianshu.io/upload_images/749674-a45e2ffca44d4f79.gif?imageMogr2/auto-orient/strip)        });      valueAnimator.setDuration(2000).start();  }</code></pre>    <p>注意LayoutParams的数值是px像素, 需要dp转换px.</p>    <p>效果</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/41ad45ddfad57fba956592b6e3ea740e.gif"></p>    <p style="text-align:center">效果</p>    <p>应用使用动画, 提升用户体验, 但要注意性能. 大量使用图片可能导致OOM; 循环动画无法释放可能产生内存泄露; 注意px与dp之间的转换.</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/37ccbf95e24c</p>    <p> </p>