View动画和属性动画

在应用中, 动画效果提升用户体验, 主要分为View动画和属性动画. View动画变换场景图片效果, 效果包括平移(translate), 缩放(scale), 旋转(rotate), 透明(alpha); 属性动画动态地改变改变属性, 达到动画效果. 本文包含源码.

Animation

本文源码的GitHub下载地址


View动画

动画包含四种平移, 缩放, 旋转, 透明, 也支持组合使用.

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));

平移动画: duration持续时间; fromXDelta起始X坐标, fromYDelta起始Y坐标; toXDelta终止X坐标, toYDelta终止Y坐标.

<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>

fillAfter动画完成后停留, 即在平移后不复原; interpolator变换插值器.

缩放动画: fromXScale起始宽度比例, fromYScale起始高度比例; toXScale终止宽度比例,toYScale终止高度比例.

<?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>

旋转动画: fromDegrees起始角度, toDegrees终止角度; pivotX旋转中心X坐标, pivotY旋转中心Y坐标.

<?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>

透明动画: fromAlpha起始透明度, toAlpha终止透明度.

<?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>

组合动画: 融合平移, 缩放, 旋转, 透明四种动画, 效果是图片旋转着从左上角滚入屏幕, 逐渐变大变清晰.

<?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>

帧动画: 特殊动画, 不断变换图片, 模拟动画效果. animation-list动画列表, 每个item表示一个图片, duration是图片停留时间. oneshot值是true持续一次, false不断循环.

<?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>

自定义动画: 重载initializeapplyTransformation方法. initialize初始化动画;applyTransformation应用转换, 参数interpolatedTime表示差值次数.

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);
    }
}

RecyclerList的项也支持动画式插入.

@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;
    }
}

属性动画

属性动画通过变换对象属性, 实现动画效果, 对应属性必须含有set和get方法, 支持调用. 对于自定义的属性, 则使用Wrapper或插值方式.

属性动画仅支持API 11以上版本, 以前版本使用支持库.

Wrapper: 根据View的变换属性, 提供宽度(width)的设置(set)与获取(get).

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();
    }
}

requestLayout: 当View确定自身不再适合现有区域时, 调用requestLayout, 要求Parent View重新调用onMeasure和onLayout重新设置当前View的位置.
特别当View的LayoutParams发生改变时, 并且值还未应用至View上, 这时候适合调用此方法.
invalidate: View本身调用迫使View重绘.

差值: 使用ValueAnimator(属性动画), 并设置更新, 添加IntEvaluator(整数估值器), 渐进地设置View的宽度.

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](https://user-gold-cdn.xitu.io/2017/1/4/5cba3a11357c16ea9129f9c7a6b9f1cc)
](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();
}

注意LayoutParams的数值是px像素, 需要dp转换px.


效果

效果

应用使用动画, 提升用户体验, 但要注意性能. 大量使用图片可能导致OOM; 循环动画无法释放可能产生内存泄露; 注意px与dp之间的转换.

OK, that's all! Enjoy it!

原文地址:https://www.cnblogs.com/wangfeng520/p/6249832.html