探索View动画和属性动画

欢迎Follow我的GitHub, 关注我的简书, 博客目录.

Animation

本文的合集已经编著成书,高级Android开发强化实战,欢迎各位读友的建议和指导。在京东即可购买:https://item.jd.com/12385680.html

Android

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

本文源码的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](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();
}

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


效果

效果

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

OK, that's all! Enjoy it!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259

推荐阅读更多精彩内容