Android属性动画——ImageView(Robots)

一、效果图


动画场景:该动画是在ImageView中显示,效果图中显示了三个动画:漂浮、左右招手,原动画是很流畅的,无丢帧,转成gif后大致能看出效果,不用在意卡顿。

二、实现原理

1.自定义Drawable,重写draw,根据onAnimationUpdate方法返回的动画进度,通过差值器计算出该帧drawable的状态,并绘制

2.调用Drawable.invalidateSelf触发View的重绘

三、代码分析

1.代码结构


2.MainActivity:测试动画Activity,无复杂逻辑

private void startAnimator(int type) {
    cancelAnimator();
    switch (type) {
        case TYPE_START:
            mAnimator = new StartRobotAnimator(this.getApplicationContext());
            break;
        case TYPE_WAVE_LEFT:
            mAnimator = new WaveRobotAnimator(this.getApplicationContext(), true);
            break;
        case TYPE_WAVE_RIGHT:
            mAnimator = new WaveRobotAnimator(this.getApplicationContext(), false);
            break;
        default:
            mAnimator = new StartRobotAnimator(this.getApplicationContext());
            break;
    }
    mImageView.setImageDrawable(mAnimator.getDrawable());
    mAnimator.start();
}

private void cancelAnimator() {
    if (mAnimator != null) {
        mAnimator.cancel();
        mAnimator = null;
    }
}

提供两个方法:开启/关闭动画


3.StartRobotAnimator:动画实现类

继承ValueAnimator,定义内部类继承Drawable


上图为StartRobotAnimator类结构,内部有较多的内部类:RobotDrawable、Body、EyeLefe...

也容易理解,Robots动画内部是多个元素在动,眼睛、身体、头、左右手。每个元素对应一个内部类。这些内部类都继承Basic。Basic中保存这些元素相同的属性。

动画绘制流程:

从ValueAnimator.start-> onAnimationUpdate-> 

Drawable.update

Drawable.onDraw.invalidateSelf -> draw

在update时,将动画进度传给Drawable,代码片段如下:

@Override
public void onAnimationUpdate(ValueAnimator animation) {
    mDrawable.update((int) (animation.getAnimatedValue()));
    mDrawable.invalidateSelf();
}

接着进入自定义Drawable的draw方法:

@Override
public void draw(@NonNull Canvas canvas) {
    if (mWidth == 0 || mHeight == 0) {
        mWidth = canvas.getWidth();
        mHeight = canvas.getHeight();
    }
    canvas.save();
    canvas.translate(mWidth / 2f - 126 / 2, mHeight / 2f - 164 / 2);//平移画布,保证绘制的robots在imageView中居中

    float T0 = DURATION;
    float t = Math.min(mTime, DURATION);
    double translateY = (-30f * t / T0 + 30) * Math.sin(3.5 * Math.PI / T0 * t + Math.PI / 2f);
    canvas.translate(0, (float) translateY);
    //绘制头、嘴巴、左右手
    mHead.draw(canvas);
    mMouth.draw(canvas, mTime);
    mHandLeft.draw(canvas, mTime);
    mHandRight.draw(canvas, mTime);
    //绘制身体、左右眼睛
    mBody.draw(canvas);
    mEyeLeft.draw(canvas, mTime);
    mEyeRight.draw(canvas, mTime);

    canvas.restore();
}

draw方法和View内部逻辑关系比较大,原理很简单,可以自己理解。按照下面的思路理解可能更方便记忆:

对画布整体做动画使用canvas.translate/rotate/scale/alpha

对某一元素做动画使用Basic类中的Matrix。

对于Matrix,可认为有三个方法,set、pre、post。set:清空矩阵之前的设置,重置矩阵 pre:左乘 post:右乘

比如要实现先平移再旋转的效果,可以setTranslate+postRotate 也可以setRotate+preTranslte。


四、源码

附上MainActivity和自定义Animator源码

package com.zmh.animation.robots;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends Activity {
    private ImageView mImageView;
    private IRobotsAnimator mAnimator;

    private final static int TYPE_START = 0x1;
    private final static int TYPE_WAVE_LEFT = 0x2;
    private final static int TYPE_WAVE_RIGHT = 0x3;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = findViewById(R.id.img);
        mAnimator = new StartRobotAnimator(this.getApplicationContext());
        mImageView.setImageDrawable(mAnimator.getDrawable());

        findViewById(R.id.btn_wave_left).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startAnimator(TYPE_WAVE_LEFT);

            }
        });

        findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startAnimator(TYPE_START);
            }
        });

        findViewById(R.id.btn_wave_right).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startAnimator(TYPE_WAVE_RIGHT);
            }
        });
    }


    private void startAnimator(int type) {
        cancelAnimator();
        switch (type) {
            case TYPE_START:
                mAnimator = new StartRobotAnimator(this.getApplicationContext());
                break;
            case TYPE_WAVE_LEFT:
                mAnimator = new WaveRobotAnimator(this.getApplicationContext(), true);
                break;
            case TYPE_WAVE_RIGHT:
                mAnimator = new WaveRobotAnimator(this.getApplicationContext(), false);
                break;
            default:
                mAnimator = new StartRobotAnimator(this.getApplicationContext());
                break;
        }
        mImageView.setImageDrawable(mAnimator.getDrawable());
        mAnimator.start();
    }

    private void cancelAnimator() {
        if (mAnimator != null) {
            mAnimator.cancel();
            mAnimator = null;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        cancelAnimator();
    }
}
package com.zmh.animation.robots;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;

class StartRobotAnimator extends ValueAnimator implements IRobotsAnimator, ValueAnimator.AnimatorUpdateListener {

    private RobotDrawable mDrawable;
    private int DURATION = 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值