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>