animator_animator api - CSDN
  • Unity动画机制 AnimatorAnimator Controller教程

    万次阅读 多人点赞 2019-05-08 11:51:52
    Unity动画机制 AnimatorAnimator Controller教程 助力快速完成 Animator 动画的添加与控制 为初学者节省宝贵的时间,避免采坑! unity 中为我们提供了而全面的动画设计解决方案,用来完成游戏对象的动态效果的控制...

    Chinar blog www.chinar.xin

    Unity动画机制 Animator Animation


    本文提供全流程,中文翻译。

    Chinar 的初衷是将一种简单的生活方式带给世人

    使有限时间 具备无限可能

    Chinar —— 心分享、心创新!

    助力快速完成 Animator 动画的添加与控制

    为初学者节省宝贵的时间,避免采坑!

    Chinar 教程效果:
    这里写图片描述



    全文高清图片,点击即可放大观看 (很多人竟然不知道)


    1

    Animator —— 动画


    Unity 中为我们提供了而全面的动画设计解决方案,用来完成游戏对象的动态效果的控制和创建

    我们想要一个人物模型动起来,非常简单

    只需要在人物的游戏对象上添加 Animator 组件

    然后创建一个 Animator Controller 来控制对应的游戏对象的动画 Animator 即可
    举个栗子黑白88
    这里写图片描述
    这里写图片描述


    2

    Animator Controller —— 动画控制器面板


    选中 Animator Controller文件,点击 Open 即可打开控制器面板

    新创建一个 Animator Controller 默认是没有任何状态的

    需要自己创建空状态,并进行相应设置
    举个栗子黑白88
    这里写图片描述


    3

    Specifies Animation —— 指定动画


    创建一个状态后,我们需要给该状态改名(为了区分状态),并指定一个动作 Motion

    这里我创建的是 站立,所以就需要找到对应的 Idel 动画,指定给 Motion
    举个栗子黑白88
    这里写图片描述
    相同流程创建跑步动作:
    这里写图片描述


    4

    Status Swithing —— 状态切换


    由于丧尸这个游戏对象,具备跑和站立 2 个状态/动作 Motion

    我们需要的是点击鼠标,它就开始移动,保持跑起来的状态

    走到目的地,它就停下,保持站立状态

    所以这里 站立——跑,状态是需要切换的,如上图,我们可以简单的建立连接

    那么我们需要添加一个 bool 参数,并设置对应条件,来使2个状态可以进行切换

    添加完成后,我们就可以通过代码,来控制人物的站立和跑 2个状态了
    举个栗子黑白88
    这里写图片描述
    具体流程:
    这里写图片描述


    5

    Code Control —— 通过代码控制状态


    完成上边所有设置,即可通过代码控制 动画状态的切换

    调用 Animator 中的函数,通过设置 bool 值,即可改变 游戏对象的 动画状态

    animator.SetBool("ZombieRun", true) 让僵尸切换为 跑 状态

    animator.SetBool("ZombieRun", false) 让僵尸切换为 站立 状态

    Animator.StringToHash("ZombieRun")可以将字符参数转为 ID(int 值) ,同样用来控制状态
    举个栗子黑白88

    using UnityEngine;
    using UnityEngine.AI;
    
    
    /// <summary>
    /// <para>作用:控制丧尸的移动</para>
    /// <para>作者:Chinar</para>
    /// <para>创建日期:2018-08-05</para>
    /// </summary>
    public class ZombieMove : MonoBehaviour
    {
        private NavMeshAgent navMeshAgent;
        private Animator     animator;
    
    
        /// <summary>
        /// 初始化函数
        /// </summary>
        void Start()
        {
            navMeshAgent = GetComponent<NavMeshAgent>(); //获取自身AI组件
            animator     = GetComponent<Animator>();     //动画组件
        }
    
    
        /// <summary>
        /// 每帧刷新
        /// </summary>
        void Update()
        {
            if (Input.GetMouseButton(1)) //右键
            {
                object     ray = Camera.main.ScreenPointToRay(Input.mousePosition); //屏幕坐标转射线
                RaycastHit hit;                                                     //射线投射碰撞
                bool       isHit = Physics.Raycast((Ray) ray, out hit);             //射线投射(射线,结构体信息) ;返回bool 值 是否检测到碰撞
                if (isHit)
                {
                    print("坐标:" + hit.point);               //射线与物体碰撞点
                    navMeshAgent.SetDestination(hit.point); //AI组件,设置目的地/终点
                    animator.SetBool("ZombieRun", true);    //让僵尸跑起来
                }
            }
            if (navMeshAgent.remainingDistance < 0.5f) //当前位置 与终点 的  剩余距离<0.5f
            {
                animator.SetBool("ZombieRun", false); //让僵尸站立
            }
        }
    }
    

    动画效果:

    这里写图片描述


    支持

    May Be —— 开发者,总有一天要做的事!


    拥有自己的服务器,无需再找攻略

    Chinar 提供一站式《零》基础教程

    使有限时间 具备无限可能!

    先点击领取 —— 阿里全产品优惠券 (享受最低优惠)


    Chinar 免费服务器、建站教程全攻略!( Chinar Blog )


    Chinar

    END

    本博客为非营利性个人原创,除部分有明确署名的作品外,所刊登的所有作品的著作权均为本人所拥有,本人保留所有法定权利。违者必究

    对于需要复制、转载、链接和传播博客文章或内容的,请及时和本博主进行联系,留言,Email: ichinar@icloud.com

    对于经本博主明确授权和许可使用文章及内容的,使用时请注明文章或内容出处并注明网址

    展开全文
  • Unity 动画系统:Animator

    千次阅读 2019-06-03 17:44:23
    Animator组件 Controller:关联到物体的Animator控制器。 Avatar:物体的Avatar。 Apply Root Motion:是使用动画本身还是使用脚本来控制角色的位置。 Update Mode:动画的更新模式。 Normal:同步更新,...

    Animator组件

       

    Controller:关联到物体的Animator控制器。

    Avatar:物体的Avatar。

    Apply Root Motion:是使用动画本身还是使用脚本来控制角色的位置。

    Update Mode:动画的更新模式。

    • Normal:同步更新,动画速度与运行速度相匹配,运行速度慢,动画慢。
    • Animate Physics:动画是有物理的相互作用时,用此模式。
    • Unscaled Time:不成比例的时间,动画忽略当前的运行速度。

    Culling Mode:动画的裁剪模式。

    • Always Animate:总是启用动画,不进行裁剪。
    • Cull Update Transforms:更新裁切。
    • Cull Completely:完全裁切。

    Animator Controller视图

    (Animation States)动画状态

       

    Motion 当前状态下的动画片段
    Speed 动画的默认速度
    Mirror 镜像
    Foot IK 是否使用Foot IK
    是否使用Foot IK 是否对没有动画的属性写回默认值
    Transitions 由当前状态出发的过渡条件列表
    • 黄色显示的状态未默认状态,指状态机首次激活时所进入的状态。可以在其他状态上右击选择Set as Layer Default State命令改变默任状态。
    • 每个状态机在检视视图中Transitions内的Solo(优先)和Mute(禁用)选择框用于控制动画在预览时的行为表现。
    • 在视图空白处右击选择Create State ---> Empty命令可以创建空动画状态。
    • 选中某状态右击,选中Copy或Delete,可以对状态进行复制或删除操作。
    • 将一段动画(Animation)拖入视图,可将动画添加到状态机中。
    • Any State:任意状态,为所有动画状态添加公共出口状态的便捷方式。但不能作为一种独立的目标状态。

    Animation Transitions(动画过渡)

    在某一动画状态右击后选择Make Transition,然后选择过渡到的下一动画状态,即可建立过渡联系。

       

    Animation Parameters(动画参数)

       

    参数值的四种基本类型:

    • Float:浮点数
    • Int:整数
    • Bool:返回布尔值,通过复选框来选择True或者False
    • Trigger:触发一个布尔值,复位控制器时消耗一个转变,由一个圆按钮表示。

    Animation Layers(动画层)

    实现同一时刻进行多种动画状态播放。

    Blend Tree(混合树)

    对两个或更多个相似的运动进行混合。

    制作:在视图右击空白处后选择Create State ---> From New Blend Tree,双击进入。

    示例:

      

    Blend Type(混合类型)

    1D混合:通过唯一的一个参数来控制子动画的混合。

    2D Simple Directional(2D简单定向模式)适用于所有动画都具有一定的运动方向、其中任何两段动画的运动方向都不相同的情形。

    2D Freeform Directional(2D自由定向模式)适用于所有动画都具有一定的运动方向,但同一方向上可以存在多段动画。

    2D Free Cartesian(2D自由笛卡尔模式)适用于动画不具有确定运动确定运动方向的情形。

    Direct:直接。让用户直接控制每个节点的权重。

     

     

     

    展开全文
  • Android动画Animator家族使用指南

    千次阅读 2018-12-27 14:23:14
    零、前言:本文知识点 ValueAnimator的认识与使用 估值器TypeEvaluator的自定义与使用 插值器TimeInterpolator的自定义与使用 Path于Animator的结合使用 ObjectAnimator的自定义...Animator家族的监听器介绍与使用...
        

    零、前言:本文知识点

    • ValueAnimator的认识与使用
    • 估值器TypeEvaluator的自定义与使用
    • 插值器TimeInterpolator的自定义与使用
    • Path于Animator的结合使用
    • ObjectAnimator的自定义与使用
    • TimeAnimator的使用
    • AnimatorSet动画集合的使用
    • Animator家族的监听器介绍与使用
    • Animator家族在xml中的使用

    一直用动画,貌似还没有好好地总结一下,趁有空,总结一波
    所谓动画,就是不停变化,在视觉上达到连续的效果
    Animator的体系并不复杂,但内部实现挺复杂的,很多类常年埋没于底层,不见天日
    如:PropertyValuesHolder及其子类Keyframes族Keyframe族KeyframeSet族
    今天试着读了一下源码,基本上读的懵懵懂懂,总的思路算是把握了


    第一节:ValueAnimator的使用

    一、简单的使用

    0.Animator家族简单认识:

    Animator是一个抽象类,不可用,只能找它的子类
    现在先看非常常用的ValueAnimator

    9414344-6d4c41873962030c.png
    Animator体系.png

    1.下面是一段ValueAnimator最简单的使用
    ValueAnimator animator = ValueAnimator.ofInt(0, 10);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Log.e(TAG, animation.getAnimatedValue()+"---");
        }
    });
    animator.start();
    

    打印结果分析:

    2018-12-26 12:04:09.290 ~ 2018-12-26 12:04:09.584---->584-290=294
    默认持续时间是300(源码中定义的),基本一致,在这段时间内不断回调onAnimationUpdate方法
    并且animation的值从预定的0~10之间不断变化,这就是ValueAnimator的基本用处
    
    2018-12-26 12:04:09.290 2001-2001/com.toly1994.animator_test E/MainActivity: 0---
    2018-12-26 12:04:09.335 2001-2001/com.toly1994.animator_test E/MainActivity: 0---
    2018-12-26 12:04:09.351 2001-2001/com.toly1994.animator_test E/MainActivity: 1---
    2018-12-26 12:04:09.373 2001-2001/com.toly1994.animator_test E/MainActivity: 1---
    2018-12-26 12:04:09.412 2001-2001/com.toly1994.animator_test E/MainActivity: 3---
    2018-12-26 12:04:09.439 2001-2001/com.toly1994.animator_test E/MainActivity: 5---
    2018-12-26 12:04:09.450 2001-2001/com.toly1994.animator_test E/MainActivity: 5---
    2018-12-26 12:04:09.468 2001-2001/com.toly1994.animator_test E/MainActivity: 6---
    2018-12-26 12:04:09.484 2001-2001/com.toly1994.animator_test E/MainActivity: 7---
    2018-12-26 12:04:09.502 2001-2001/com.toly1994.animator_test E/MainActivity: 8---
    2018-12-26 12:04:09.517 2001-2001/com.toly1994.animator_test E/MainActivity: 8---
    2018-12-26 12:04:09.534 2001-2001/com.toly1994.animator_test E/MainActivity: 9---
    2018-12-26 12:04:09.568 2001-2001/com.toly1994.animator_test E/MainActivity: 9---
    2018-12-26 12:04:09.584 2001-2001/com.toly1994.animator_test E/MainActivity: 10---
    

    2.从中衍生的想法

    1).不断调用onAnimationUpdate回调
    2).可以获取有规律变化的不同的数值
    在自定义View中onAnimationUpdate刷新界面,并动态改变数值

    9414344-b85736c8ae6e95f4.gif
    简单应用.gif
    /**
     * 作者:张风捷特烈<br/>
     * 时间:2018/12/26 0026:7:50<br/>
     * 邮箱:1981462002@qq.com<br/>
     * 说明:Animator测试View
     */
    public class AnimatorView extends View {
        private static final String TAG = "AnimatorView";
        
        private Paint mPaint;//画笔
        private int mRadius = 100;//小球初始半径
        private ValueAnimator mAnimator;//动画器
    
        public AnimatorView(Context context) {
            this(context, null);
        }
    
        public AnimatorView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(0xff94E1F7);
    
            mAnimator = ValueAnimator.ofInt(100, 300);
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mRadius= (int) animation.getAnimatedValue();
                    invalidate();
                }
            });
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.translate(400, 400);//移动坐标
            canvas.drawCircle(0, 0, mRadius, mPaint);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Log.e(TAG, "onTouchEvent: ");
                    mAnimator.start();//点击开启动画
                    break;
                case MotionEvent.ACTION_UP:
            }
            return super.onTouchEvent(event);
        }
    }
    

    其实道理很简单,就是把打印输出换成了刷新视图,而且半径在不断变化


    3.常规配置

    看一下RESTART(默认)和REVERSE的区别

    RESTART REVERSE
    9414344-4322a489906e55b5.gif
    9414344-a148aee6ee8d62a2.gif
    mAnimator.setStartDelay(1000);//设置延迟
    mAnimator.setRepeatCount(2);//设置重复执行次数
    // mAnimator.setRepeatMode(ValueAnimator.RESTART);//重新开始100->300 100->300
    mAnimator.setRepeatMode(ValueAnimator.REVERSE);//反转开始100->300 300->100
    mAnimator.setDuration(1000);//设置时长
    

    二、ofArgbofObject

    颜色变化 颜色大小
    9414344-67b2ff4e4cf8b09d.gif
    9414344-678cf6bf75f037d7.gif

    1.改变颜色:ofArgb

    传入两个颜色(起始色和终止色)

    mColorAnimator = ValueAnimator.ofArgb(0xff94E1F7, 0xffF35519);
    mColorAnimator.setDuration(500);//设置时长
    mColorAnimator.setRepeatCount(1);//设置重复执行次数
    mColorAnimator.setRepeatMode(ValueAnimator.REVERSE);
    
    mColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mPaint.setColor((Integer) animation.getAnimatedValue());
            invalidate();
        }
    });
    

    2.如何即改变大小又改变颜色:

    ValueAnimator.ofObject + TypeEvaluator

    2.1先定义一个类承载数据:Ball(为了演示简洁,使用public属性)
    public class Ball {
        public int color;
        public int r;
    
        public Ball() {
        }
    
        public Ball(int r, int color) {
            this.color = color;
            this.r = r;
        }
    }
    

    2.2.创建TypeEvaluator(类型估值器)

    TypeEvaluator是确定对象的各个属性如何变化,看下面例子:
    这里fraction是分率,startValue和endValue分别是起始和终止对象的状态

    public class BallEvaluator implements TypeEvaluator {
        @Override
        public Object evaluate(float fraction, Object startValue, Object endValue) {
            Ball start = (Ball) startValue;//小球初始状态
            Ball end = (Ball) endValue;//小球终止状态
            
            Ball ball = new Ball();//当前小球
            //半径=初始+分率*(结尾-初始) 比如运动到一半,分率是0.5
            ball.r = (int) (start.r + fraction * (end.r - start.r));
            //颜色怎么渐变?
            ball.color = evaluateColor(fraction, start.color, end.color);
            return null;
        }
        
        /**
         * 根据分率计算颜色
         */
        private int evaluateColor(float fraction, Object startValue, Object endValue) {
            int startInt = (Integer) startValue;
            float startA = ((startInt >> 24) & 0xff) / 255.0f;
            float startR = ((startInt >> 16) & 0xff) / 255.0f;
            float startG = ((startInt >> 8) & 0xff) / 255.0f;
            float startB = (startInt & 0xff) / 255.0f;
    
            int endInt = (Integer) endValue;
            float endA = ((endInt >> 24) & 0xff) / 255.0f;
            float endR = ((endInt >> 16) & 0xff) / 255.0f;
            float endG = ((endInt >> 8) & 0xff) / 255.0f;
            float endB = (endInt & 0xff) / 255.0f;
    
            // convert from sRGB to linear
            startR = (float) Math.pow(startR, 2.2);
            startG = (float) Math.pow(startG, 2.2);
            startB = (float) Math.pow(startB, 2.2);
    
            endR = (float) Math.pow(endR, 2.2);
            endG = (float) Math.pow(endG, 2.2);
            endB = (float) Math.pow(endB, 2.2);
    
            // compute the interpolated color in linear space
            float a = startA + fraction * (endA - startA);
            float r = startR + fraction * (endR - startR);
            float g = startG + fraction * (endG - startG);
            float b = startB + fraction * (endB - startB);
    
            // convert back to sRGB in the [0..255] range
            a = a * 255.0f;
            r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
            g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
            b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
    
            return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
        }
    }
    

    看源码中怎么渐变颜色的:ArgbEvaluator.getInstance()
    可以看到有个计算颜色的方法,拿来用呗(我直接拷过去用)

    public static ValueAnimator ofArgb(int... values) {
        ValueAnimator anim = new ValueAnimator();
        anim.setIntValues(values);
        anim.setEvaluator(ArgbEvaluator.getInstance());
        return anim;
    }
    
    ---->[计算颜色方法evaluate]---------------
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        //计算颜色方法详情......
    }
    

    3.使用估值器指定曲线方程运动

    该方程是二次曲线:y=x*x/800 当然你也可以定义自己喜欢的方程

    9414344-9936bae333328508.gif
    指定曲线方程运动.gif
    public class Ball {
        public int color;
        public int r;
        public int x;
        public int y;
    
        public Ball() {
        }
    
        public Ball(int r, int color) {
            this.color = color;
            this.r = r;
        }
    
        public Ball(int r, int color, int x, int y) {
            this.color = color;
            this.r = r;
            this.x = x;
            this.y = y;
        }
    }
    

    估值器修改:

    public class BallEvaluator implements TypeEvaluator {
        @Override
        public Object evaluate(float fraction, Object startValue, Object endValue) {
            Ball start = (Ball) startValue;
            Ball end = (Ball) endValue;
            Ball ball = new Ball();
            ball.color = evaluateColor(fraction, start.color, end.color);
            ball.r = (int) (start.r + fraction * (end.r - start.r));
            ball.x = (int) (start.x + fraction * (end.x - start.x));
            ball.y= ball.x*ball.x/800;//此处依赖x确定y值
            return ball;
        }
    }
    

    AnimatorView

    public class AnimatorView extends View {
        private static final String TAG = "AnimatorView";
        private Paint mPaint;
        private int mRadius = 50;
        private int dx;
        private int dy;
    
        private ValueAnimator mAnimator;
        private ValueAnimator mColorAnimator;
        private ValueAnimator mObjAnimator;
    
        public AnimatorView(Context context) {
            this(context, null);
        }
    
        public AnimatorView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(0xff94E1F7);
            Ball startBall = new Ball(50, 0xff94E1F7,0,0);
            Ball endBall = new Ball(100, 0xffF35519,500,1000);
            mObjAnimator = ValueAnimator.ofObject(new BallEvaluator(), startBall, endBall);
    
            mObjAnimator.setRepeatMode(ValueAnimator.REVERSE);//反转开始100->300 300->100
            mObjAnimator.setDuration(1000);//设置时长
            mObjAnimator.setRepeatCount(1);//设置重复执行次数
            mObjAnimator.setRepeatMode(ValueAnimator.REVERSE);
    
            mObjAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    Ball ball = (Ball) animation.getAnimatedValue();
                    mRadius = ball.r;
                    mPaint.setColor(ball.color);
                    dx=ball.x;
                    dy=ball.y;
                    Log.e(TAG, "onAnimationUpdate: "+dx+":"+dy);
                    invalidate();
                }
            });
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.translate(dx, dy);
            canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mObjAnimator.start();
                    break;
                case MotionEvent.ACTION_UP:
            }
    
            return super.onTouchEvent(event);
    
        }
    }
    
    

    基本套路就是这样,有了ofObject,属性随意变,还怕动画吗?
    核心就是估值器的定义,其实ofInt,ofFloat,ofArgb只是适用了内置估值器而已
    本质上和ofObject并没有什么不同,可以看成单属性的简易版ofObject


    三、插值器

    如果估值器TypeEvaluator告诉你给怎么跑,那么插值器则告诉你跑多快
    下面演示一下三个内置插值器(内置还有几个,自己试试)和自定义的三个插值器

    9414344-51c5df51998cac64.gif
    插值器.gif

    1.自定义插值器:sin型先快后慢

    这里的input是从0~1变化的值,插值器就是改变input值的变化情况

    public class D_Sin_Inter implements TimeInterpolator {
        @Override
        public float getInterpolation(float input) {
            //input是一个从0~1均匀变化的值
            //从0到PI/2均匀变化的值
            float rad = (float) (Math.PI/2 * input);
            //返回这个弧度的sin值--sin曲线在0~PI/2区域是增长越来越缓慢,小球运动越来越缓慢
            return (float) (Math.sin(rad));
        }
    }
    

    2.自定义插值器:sin型先满后快
    public class A_Sin_Inter implements TimeInterpolator {
        @Override
        public float getInterpolation(float input) {
            //input是一个从0~1均匀变化的值
            //从0到PI/2均匀变化的值
            float rad = (float) (Math.PI/2 * input+Math.PI/2);
            //返回这个弧度的sin值--sin曲线在PI/2~PI区域是降低越来越快
            return (float) (1-(Math.sin(rad)));//返回1-
        }
    }
    

    3.自定义插值器:log型
    /**
     * 作者:张风捷特烈<br/>
     * 时间:2018/12/26 0026:20:41<br/>
     * 邮箱:1981462002@qq.com<br/>
     * 说明:Log型先快后慢
     */
    public class D_Log_Inter implements TimeInterpolator {
        @Override
        public float getInterpolation(float input) {
            return (float) (Math.log10(1 + 9 * input));
        }
    }
    

    插值器实际上就是基于input加工,时间流动(每次刷新间隔)是基本恒定的,
    input是从0~1均匀变化的,通过input将其映射到一组对应关系上,就像数学中的函数
    input是x,称为自变量,因变量y由函数式和x确定,返回值便是y,供代码中使用(D_Sin_Inter如下)
    LinearInterpolator线性插值器也就是x=y,而已,本质是一样的

    9414344-e8f8ef70c5d9da1c.png

    4.优雅的实现测试代码

    只需在名字数组和插值器数组里对应添加即可,其他会自动处理

    public class AnimatorInterView extends View {
        private static final String TAG = "AnimatorView";
    
        private Paint mPaint;
        private int mRadius = 50;
        private int dx[];
        private String[] mStrings;
        private TimeInterpolator[] mInterpolators;
    
        public AnimatorInterView(Context context) {
            this(context, null);
        }
    
        public AnimatorInterView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(0xff94E1F7);
            mPaint.setTextSize(40);
            mStrings = new String[]{"Linear", "Bounce", "AOI", "OI", "D_sin", "D_log", "A_sin", "A_log"};
            mInterpolators = new TimeInterpolator[]{
                    new LinearInterpolator(),
                    new BounceInterpolator(),
                    new AnticipateOvershootInterpolator(),
                    new OvershootInterpolator(),
                    new D_Sin_Inter(),
                    new D_Log_Inter(),
                    new A_Sin_Inter()};
            dx = new int[mInterpolators.length];
        }
    
        private ValueAnimator createAnimator(int index, TimeInterpolator interpolator) {
            ValueAnimator mAnimator = ValueAnimator.ofInt(0, 800);
            mAnimator.setRepeatCount(1);//设置重复执行次数
            mAnimator.setRepeatMode(ValueAnimator.REVERSE);//反转开始100->300 300->100
            mAnimator.setDuration(3000);//设置时长
            mAnimator.setInterpolator(interpolator);
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    dx[index] = (int) animation.getAnimatedValue();
                    invalidate();
                }
            });
            return mAnimator;
        }
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            for (int i = 0; i < dx.length; i++) {
                canvas.translate(0, 120);
                mPaint.setColor(0xff94E1F7);
                canvas.drawCircle(mRadius + dx[i], mRadius, mRadius, mPaint);
                mPaint.setColor(0xff000000);
                mPaint.setStrokeWidth(4);
                canvas.drawLine(mRadius, mRadius, 800 + mRadius, mRadius, mPaint);
                canvas.drawText(mStrings[i], 800 + 3 * mRadius, mRadius, mPaint);
            }
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    for (int i = 0; i < mInterpolators.length; i++) {
                        createAnimator(i, mInterpolators[i]).start();
                    }
                    break;
                case MotionEvent.ACTION_UP:
            }
            return super.onTouchEvent(event);
        }
    }
    

    [插曲]:路径于Animator的结合

    核心是使用PathMeasure和DashPathEffect对路径的长度进行控制
    关于Path的这方面知识,这里不做详解,详见:Android关于Path你所知道的和不知道的一切

    9414344-bcbdd3a8088e2ac7.gif
    路径动画.gif
    /**
     * 作者:张风捷特烈<br/>
     * 时间:2018/12/26 0026:7:50<br/>
     * 邮箱:1981462002@qq.com<br/>
     * 说明:Animator与Path
     */
    public class AnimatorPathView extends View {
        private static final String TAG = "AnimatorView";
    
        private Paint mPaint;
        private Path mPath;
        private PathMeasure pathMeasure;
    
        public AnimatorPathView(Context context) {
            this(context, null);
        }
    
        public AnimatorPathView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(0xff94E1F7);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(10);
            mPaint.setStrokeJoin(Paint.Join.ROUND);
            //测量路径
            mPath = new Path();
            mPath = nStarPath(mPath, 8, 250, 160);//八角形路径
            pathMeasure = new PathMeasure(mPath, false);
        }
    
        private ValueAnimator createAnimator() {
            ValueAnimator mAnimator = ValueAnimator.ofInt(0, 800);
            mAnimator.setRepeatCount(1);//设置重复执行次数
            mAnimator.setRepeatMode(ValueAnimator.REVERSE);//反转开始100->300 300->100
            mAnimator.setDuration(3000);//设置时长
            mAnimator.setInterpolator(new AnticipateOvershootInterpolator());
    
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = animation.getAnimatedFraction();
                    //核心:创建DashPathEffect
                    DashPathEffect effect = new DashPathEffect(
                            new float[]{
                                    pathMeasure.getLength(),
                                    pathMeasure.getLength()},
                            value * pathMeasure.getLength());
                    mPaint.setPathEffect(effect);
                    invalidate();
                }
            });
            return mAnimator;
        }
    
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.translate(250, 250);
            canvas.drawPath(mPath, mPaint);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    createAnimator().start();
                    break;
                case MotionEvent.ACTION_UP:
            }
            return super.onTouchEvent(event);
        }
    
        /**
         * n角星路径
         *
         * @param num 几角星
         * @param R   外接圆半径
         * @param r   内接圆半径
         * @return n角星路径
         */
        public static Path nStarPath(Path path, int num, float R, float r) {
            float perDeg = 360 / num;
            float degA = perDeg / 2 / 2;
            float degB = 360 / (num - 1) / 2 - degA / 2 + degA;
            path.moveTo((float) (Math.cos(rad(degA)) * R), (float) (-Math.sin(rad(degA)) * R));
            for (int i = 0; i < num; i++) {
                path.lineTo(
                        (float) (Math.cos(rad(degA + perDeg * i)) * R),
                        (float) (-Math.sin(rad(degA + perDeg * i)) * R));
                path.lineTo(
                        (float) (Math.cos(rad(degB + perDeg * i)) * r),
                        (float) (-Math.sin(rad(degB + perDeg * i)) * r));
            }
            path.close();
            return path;
        }
    
        /**
         * 角度制化为弧度制
         *
         * @param deg 角度
         * @return 弧度
         */
        public static float rad(float deg) {
            return (float) (deg * Math.PI / 180);
        }
    }
    

    第二节:ValueAnimator之子ObjectAnimator和TimeAnimator

    作为孩子,它老爸能做的它也能做,并且还会有一些自己的特长
    ObjectAnimator针对有setXxx方法的属性,进行的"Xxx"属性变化动画
    注:Xxx的首字母大小写都可以


    一、View内置属性的测试

    1.简单入门--下移示例:
    9414344-a7ecae114af55741.gif
    下移动.gif
    private ObjectAnimator mMoveDown;//下移动画
    
    mMoveDown = ObjectAnimator//创建实例
            //(View,属性名,初始化值,结束值)
            .ofFloat(this, "translationY", 0, 300)
            .setDuration(1000);//设置时常
    
    @Override//绘制方法
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(50, 50, 50, mPaint);
    }
    
    mMoveDown.start();//开启动画
    

    加上背景看一下,可以看出是整个View进行了变化。

    9414344-28132b3fcafc0602.gif
    加背景.gif

    2.常用属性一览:
    属性名 演示 解释
    alpha
    9414344-361b9db606f03958.gif
    透明度1~0
    translationX
    9414344-c19ac1d9ab0d4330.gif
    X方向移动
    translationY
    9414344-888a59601da0ea79.gif
    Y方向移动
    rotation
    9414344-ba58fdbe672f41b4.gif
    旋转(默认View中心点)
    rotationX
    9414344-4eaa00d4731c400c.gif
    X轴旋转(默认View中心横轴)
    rotationY
    9414344-9fd22ee1c58382f3.gif
    Y轴旋转(默认View中心纵轴)
    scaleX
    9414344-7e65e550bcc6cccd.gif
    X缩放 倍数
    scaleY
    9414344-a1552e517c9ab78a.gif
    Y缩放 倍数

    3.旋转、缩放中心点设置:
    setPivotX(200);
    setPivotY(200);
    
    9414344-f9095bb28582ce1b.gif
    旋转中心点.gif

    4.多参数情况(多参情况Animator家族皆适用)

    0-->360 360-->0 0-->90

    .ofFloat(this, "rotation", 0, 360,360,0,0,90)
    
    9414344-0a624776a2ee80ac.gif
    多参数.gif

    二、自定义ObjectAnimator属性

    内置的只是一些常用的,我们也可以自定义自己的属性

    1.自定义圆的大小动画

    必须用一个setXxx的方法,属性名则为xxx,调用重绘方法

    public void setRadius(int radius) {
        mRadius = radius;
        invalidate();//记得重绘
    }
    
    ObjectAnimator//创建实例
            //(View,属性名,初始化值,结束值)
            .ofInt(this, "Radius", 100, 50,100,20,100)
            .setDuration(3000);//设置时常
    
    9414344-17adabf8ad0b5a2e.gif
    自定义半径.gif

    2.自定义颜色动画
    public void setColor(int color) {
        mColor = color;
        mPaint.setColor(mColor);
        invalidate();//记得重绘
    }
    
    colorAnimator = ObjectAnimator//创建实例
             //(View,属性名,初始化值,结束值)
             .ofInt(this, "color", 0xff0000ff, 0xffF2BA38, 0xffDD70BC)
             .setDuration(3000);
    colorAnimator.setEvaluator(new ArgbEvaluator());//颜色的估值器
    
    9414344-22fac2a00af843af.gif
    自定义颜色.gif

    3.ValueAnimator和ObjectAnimator的区别在哪?
    1.ValueAnimator需要手动添加监听,手动获取ValueAnimator的数据,手动书写变更逻辑
    2.ObjectAnimator可以不用进行更新监听,核心在`setXxx`里进行,  
    也就是每次更新时会自己走setXxx里的方法,这样方便在外部使用来动态改变属性
    3.ValueAnimator的灵活性要好,毕竟自己动手,可以脑洞大开,想怎么玩怎么玩
    4.ObjectAnimator针对有setXxx的属性进行动画,两者的侧重点不同  
    5.总的来说ObjectAnimator向于应用(简洁,快速),ValueAnimator偏向于操作(灵活,多变)
    

    三、TimeAnimator

    这个类总共代码100行,而且几乎一半都是注释
    它继承自ValueAnimator,可谓也是Animator家族的掌上明珠,但非常纯真与专注
    她想做的只有一件事:提供一条时间流(每个16或17ms回调一次方法)

    mAnimator = new TimeAnimator();
    (自己,运行总时长,每次回调的时间间隔)
    mAnimator.setTimeListener((animation, totalTime, deltaTime) -> {
        Log.e(TAG, "totalTime:" + totalTime + ",  deltaTime:" + deltaTime);
        if (totalTime > 300) {
            animation.pause();
        }
    });
    

    运行结果:

    2018-12-27 10:09:35.047  E/TimeAnimatorView: totalTime:0,  deltaTime:0
    2018-12-27 10:09:35.051  E/TimeAnimatorView: totalTime:2,  deltaTime:2
    2018-12-27 10:09:35.068  E/TimeAnimatorView: totalTime:19,  deltaTime:17
    2018-12-27 10:09:35.085  E/TimeAnimatorView: totalTime:36,  deltaTime:17
    2018-12-27 10:09:35.101  E/TimeAnimatorView: totalTime:52,  deltaTime:16
    2018-12-27 10:09:35.118  E/TimeAnimatorView: totalTime:69,  deltaTime:17
    2018-12-27 10:09:35.135  E/TimeAnimatorView: totalTime:86,  deltaTime:17
    2018-12-27 10:09:35.151  E/TimeAnimatorView: totalTime:102,  deltaTime:16
    2018-12-27 10:09:35.167  E/TimeAnimatorView: totalTime:119,  deltaTime:17
    2018-12-27 10:09:35.184  E/TimeAnimatorView: totalTime:136,  deltaTime:17
    2018-12-27 10:09:35.200  E/TimeAnimatorView: totalTime:152,  deltaTime:16
    2018-12-27 10:09:35.218  E/TimeAnimatorView: totalTime:169,  deltaTime:17
    2018-12-27 10:09:35.234  E/TimeAnimatorView: totalTime:186,  deltaTime:17
    2018-12-27 10:09:35.251  E/TimeAnimatorView: totalTime:202,  deltaTime:16
    2018-12-27 10:09:35.268  E/TimeAnimatorView: totalTime:219,  deltaTime:17
    2018-12-27 10:09:35.284  E/TimeAnimatorView: totalTime:236,  deltaTime:17
    2018-12-27 10:09:35.300  E/TimeAnimatorView: totalTime:252,  deltaTime:16
    2018-12-27 10:09:35.318  E/TimeAnimatorView: totalTime:269,  deltaTime:17
    2018-12-27 10:09:35.334  E/TimeAnimatorView: totalTime:286,  deltaTime:17
    2018-12-27 10:09:35.350  E/TimeAnimatorView: totalTime:303,  deltaTime:17
    

    这样关于ValueAnimator基本上就结束了(还有几个监听,最后一起将)


    四、AnimatorSet

    综合前几次的动画效果,拼装在一起,AnimatorSet本身并不难

    1.Builder模式的AnimatorSet

    源码一翻,可见里面有个Builder,可就是建造者模式了,
    每个动画在AnimatorSet中是一个Node,Budiler中的方法就是:
    为处理当前节点和插入节点的关系,看下面一组动画 :

    9414344-9599a0eb03f5ff01.gif
    动画集合.gif
    mSet//半径-->移动+渐变-->变色
            .play(translationX)//移动
            .with(alpha)//渐变
            .after(radiusAnimator)//半径
            .before(colorAnimator);//变色
    

    测试源码:

    public class AnimatorSetView extends View {
        private static final String TAG = "AnimatorView";
        private Paint mPaint;
        private int mRadius = 50;
        private int mColor = 50;
        private ObjectAnimator colorAnimator;
        private ObjectAnimator radiusAnimator;
        ObjectAnimator translationX;
        ObjectAnimator alpha;
        private AnimatorSet mSet;
    
        public AnimatorSetView(Context context) {
            this(context, null);
        }
    
        public AnimatorSetView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(0xff94E1F7);
            mSet = new AnimatorSet();
            translationX = ObjectAnimator//创建实例
                    //(View,属性名,初始化值,结束值)
                    .ofFloat(this, "translationX", 0, 300, 150, 100, 20, 100)
                    .setDuration(3000);//设置时常
            alpha = ObjectAnimator//创建实例
                    //(View,属性名,初始化值,结束值)
                    .ofFloat(this, "alpha", 1, 0.5f, 1, 0, 1)
                    .setDuration(3000);//设置时常
            radiusAnimator = ObjectAnimator//创建实例
                    //(View,属性名,初始化值,结束值)
                    .ofInt(this, "Radius", 50, 100, 50, 100, 20, 100)
                    .setDuration(3000);//设置时常
            colorAnimator = ObjectAnimator//创建实例
                    //(View,属性名,初始化值,结束值)
                    .ofInt(this, "color", 0xff0000ff, 0xffF2BA38, 0xffDD70BC)
                    .setDuration(3000);
            colorAnimator.setEvaluator(new ArgbEvaluator());//颜色的估值器
            mSet//半径-->移动+渐变-->变色
                    .play(translationX)
                    .with(alpha)
                    .after(radiusAnimator)
                    .before(colorAnimator);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mSet.start();
                    break;
                case MotionEvent.ACTION_UP:
            }
            return super.onTouchEvent(event);
        }
    
        public void setRadius(int radius) {
            mRadius = radius;
            setMeasuredDimension(mRadius * 2, mRadius * 2);
            invalidate();//记得重绘
        }
    
        public void setColor(int color) {
            mColor = color;
            mPaint.setColor(mColor);
            invalidate();//记得重绘
        }
    }
    

    2.AnimatorSet自身方法:

    顾名思义:也就是一起运动还是分批运动

    mSet.playTogether(translationX,alpha,radiusAnimator,colorAnimator);
    mSet.playSequentially(translationX,alpha,radiusAnimator,colorAnimator);
    

    四、Animator的监听:

    可见Animator有两个内部接口,AnimatorListenerAnimatorPauseListener
    AnimatorListenerAdapter是两个接口的空实现类,标准适配器模式。
    ValueAnimator作为孩子,有自己的一个接口AnimatorUpdateListener

    9414344-0bbe61f3fdf9131d.png
    监听接口关系.png
    1、AnimatorListener:动画监听

    Animator中的监听器两个孩子也都能用

       //动画开启时回调
        void onAnimationStart(Animator animation);
        //动画结束时回调
        void onAnimationEnd(Animator animation);
        //动画取消时回调
        void onAnimationCancel(Animator animation);
        //重复时回调
        void onAnimationRepeat(Animator animation);
    

    2.动画测试

    开始时设为绿色-->重复时设为随机色-->取消是大小变为50-->结束时设为蓝色

    9414344-4b1cf4f4653e35e6.gif
    动画监听.gif
    mTranslationX = translationX();
    mTranslationX.setRepeatMode(ValueAnimator.REVERSE);
    mTranslationX.setRepeatCount(ValueAnimator.INFINITE);
    
    mTranslationX.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {
            //开始时设为绿色
            setColor(Color.GREEN);
        }
    
        @Override
        public void onAnimationEnd(Animator animation) {
            //结束时设为蓝色
            setColor(Color.BLUE);
        }
    
        @Override
        public void onAnimationCancel(Animator animation) {
            //取消时大小变为50
            setCircleR(50);
        }
    
        @Override
        public void onAnimationRepeat(Animator animation) {
            //重复时设为随机色
            setColor(ColUtils.randomColor());
        }
    });
    mTranslationX.start();
    
     mTranslationX.cancel();//取消动画
    

    3、AnimatorPauseListener:动画暂停监听
    //暂停回调
    void onAnimationPause(Animator animation);
    //恢复回调
    void onAnimationResume(Animator animation);
    

    效果如下:点击运动,右滑暂停颜色变黄,下滑恢复颜色变蓝

    9414344-badc83e9fa907bd7.gif
    暂停监听.gif
    mTranslationX.addPauseListener(new Animator.AnimatorPauseListener() {
        @Override
        public void onAnimationPause(Animator animation) {
            setColor(Color.YELLOW);//暂停黄色
        }
        @Override
        public void onAnimationResume(Animator animation) {
            setColor(Color.BLUE);//恢复蓝色
        }
    });
    

    4、AnimatorUpdateListener: ValueAnimator一系专有监听
    //更新时回调
    void onAnimationUpdate(ValueAnimator animation);
    

    效果如下:每当更新是将半径和位移联动

    9414344-099cd91715283c78.gif
    更新监听.gif
    mTranslationX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mCircleR = (Float) animation.getAnimatedValue();
            invalidate();
        }
    });
    

    五、Animator家族在xml中的使用:

    在res下创建:animator文件夹

    1.Animator标签

    直接用animator标签感觉也有点麻烦,这里看一下吧

    xml中属性 含义 代码中对应
    duration 播放的时长 setDuration()
    valueType 参数值类型 ofXXX
    valueFrom 初始值 ofXXX(第1参)
    valueTo 结束值 ofXXX(第2参)
    startOffset 延时 startDelay()
    repeatCount 重复次数 setRepeatCount()
    interpolator 插值器 setRepeatMode()

    1.1.animator.xml
    9414344-5fa0a7537809dbaa.gif
    xml中使用.gif
    <?xml version="1.0" encoding="utf-8"?>
    <animator
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="2000"
        android:repeatCount="2"
        android:repeatMode="reverse"
        android:startOffset="1000"
        android:valueFrom="0dp"
        android:valueType="floatType"
        android:valueTo="200dp">
    </animator>
    

    1.2.布局
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        <View
            android:id="@+id/id_btn_go"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginStart="24dp"
            android:layout_marginTop="32dp"
            android:background="#3ED7FA"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
    </android.support.constraint.ConstraintLayout>
    

    1.3.代码中使用:MainActivity

    由Xml获取ValueAnimator,之后的事,就自己动手,感觉有点麻烦

    View button = findViewById(R.id.id_btn_go);
    ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.animator);
    
    animator.addUpdateListener(anim->{
        float animatedValue = (float) anim.getAnimatedValue();
        button.setTranslationX(animatedValue);
    });
    
    button.setOnClickListener((v)->{
        animator.start();
    });
    

    2.setobjectAnimator标签

    objectAnimator多了一个propertyName属性,其余一致

    9414344-9ed25097105c1d95.gif

    2.1set_obj_animator.xml
    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:ordering="sequentially">
        <objectAnimator
            android:duration="1500"
            android:propertyName="rotationY"
            android:valueFrom="0"
            android:valueTo="180"/>
        <objectAnimator
            android:duration="1500"
            android:propertyName="alpha"
            android:valueFrom="0.3f"
            android:valueTo="1f"/>
        <objectAnimator
            android:duration="1500"
            android:propertyName="translationX"
            android:valueFrom="0"
            android:valueTo="180dp"/>
    </set>
    

    2.2:代码中使用
    View button = findViewById(R.id.id_btn_go);
    Animator set_obj = AnimatorInflater.loadAnimator(this, R.animator.set_obj_animator);
    et_obj.setTarget(button);
            
    button.setOnClickListener((v)->{
        set_obj.start();
    });
    

    3、最后看一下我大objectAnimator变换路径

    详情可见:Android资源res之矢量图完全指南(加SVG-path命令分析)

    9414344-d69ed503957efe90.png
    path.png
    箭头:M8,50, l100,0 M0,47, l40,40 M0,52 l40 -40
    菜单:M0,50, l80,0 M0,80, l80,0 M0,20 l80 0
    
    path变形 变形+旋转
    9414344-7b94fa21676e307d.gif
    9414344-a3ca8367d2239e64.gif

    1.将两个path字符串放入string.xml

    直接写也可以,但复用不方便

    <resources>
        <string name="app_name">test</string>
        <string name="path_from">M8,50, l100,0 M0,47, l40,40 M0,52 l40 -40 </string>
        <string name="path_to">M0,50, l80,0 M0,80, l80,0 M0,20 l80 0</string>
    </resources>
    

    2.矢量图:path_test.xml
    <?xml version="1.0" encoding="utf-8"?>
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
            android:width="48dp"
            android:height="48dp"
            android:viewportWidth="100"
            android:viewportHeight="100">
        <group
            android:translateX="4"
            android:translateY="4">
            <path
                android:pathData="M0,0 A30,50,90,0,1,50,50"
                android:strokeWidth="4"
                android:strokeColor="@color/black"/>
        </group>
    </vector>
    

    3.旋转动画:rotation_animator.xml
    <?xml version="1.0" encoding="utf-8"?>
    <objectAnimator
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:propertyName="rotation"
        android:valueFrom="0"
        android:valueTo="180"/>
    
    

    4.路径动画:path_animator.xml
    <?xml version="1.0" encoding="utf-8"?>
    
    <objectAnimator
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:duration="1000"
        android:interpolator="@android:interpolator/linear"
        android:propertyName="pathData"
        android:valueFrom="@string/path_from"
        android:valueTo="@string/path_to"
        android:valueType="pathType"/>
    

    5.矢量图文件:icon_path.xml
    <?xml version="1.0" encoding="utf-8"?>
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
            android:width="48dp"
            android:height="48dp"
            android:viewportWidth="100"
            android:viewportHeight="100">
        <group android:name="container"
            android:translateX="8"
            android:pivotX="50"
               android:scaleY="0.8"
               android:scaleX="0.8"
            android:pivotY="50">
    
            <path
                android:name="alpha_anim"
                android:pathData="@string/path_from"
                android:strokeWidth="8"
                android:strokeColor="#000"/>
        </group>
    </vector>
    

    6.整合动画:anima_path.xml
    <animated-vector
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/icon_path">
        <target
            android:name="alpha_anim"
            android:animation="@animator/path_animator"/>
        <target
            android:name="container"
            android:animation="@animator/rotation_animator">
        </target>
    </animated-vector>
    

    7.使用动画:
     <ImageView
         android:id="@+id/id_iv"
         android:layout_width="200dp"
         android:layout_height="200dp"
         android:src="@drawable/anima_path"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"/>
    
    //点击时:
    Drawable drawable = mIdIv.getDrawable();
    if (drawable instanceof Animatable){
        ((Animatable) drawable).start();
    }
    

    ok,这样就行了,你可以随意定制两个路径,但必须保证两个路径的指令相同,不然会崩


    后记:捷文规范

    1.本文成长记录及勘误表
    项目源码 日期 备注
    V0.1--github 2018-12-27 Android动画Animator家族使用指南
    2.更多关于我
    笔名 QQ 微信 爱好
    张风捷特烈 1981462002 zdl1994328 语言
    我的github 我的简书 我的掘金 个人网站
    3.声明

    1----本文由张风捷特烈原创,转载请注明
    2----欢迎广大编程爱好者共同交流
    3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
    4----看到这里,我在此感谢你的喜欢与支持


    9414344-8a0c95a090041a0d.png
    icon_wx_200.png
    展开全文
  • Android Animator(Android动画)

    千次阅读 2016-09-20 11:24:54
    Android动画分为两类: 1.View Animation(视图动画,在api1引入)View Animation又分为两类:Frame animation(幁动画) 和Tween animation (补间动画) 2.Property Animator(属性动画,在api11引入)

    前言:又是很久没有写blog了,原本想看完《Android群英转》在来总结一下的,但是看到后面突然发现前面看过的东西都忘记了。突然发现还是要总结一下。。。。一定要总结!一定要总结!因为我发现很多以前我写过的很多代码都忘记了。。。很多需要注意的坑都要重新去看文档填补!所以一定要养成总结的习惯!这样提高会快很多!今天简单的记录一下android里面的动画吧!

    Android动画分为两类:

    1.View Animation(视图动画,在api1引入)View Animation又分为两类:Frame animation(幁动画) 和Tween animation (补间动画)

    2.Property Animator(属性动画,在api11引入)

    这两者的区别就是:

    1.View Animation能做的,Property Animator也能做,View Animation做不到的,Property Animator也能做!比如修改背景颜色,比如View Animation要求使用的对象是一个View对象,而Property Animator却不需要,因为他主要修改的是对象属性

    2.View Animation交互性不好,比如你对TextView使用了动画从(0,0)点移动到(400,400),虽然视图上textView移动到了(400,400),但是textview的点击响应事件却在(0  ,0)点。关于这点大家可以试验下印象深刻点。而Property Animator却没有这个问题(因为他修改的textview的属性)。

    3.Property Animator性能方面比View Animation要高一点。

    主要记录下Property Animator的简单使用

    Property Animator顾名思义就是修改属性的,主要是通过不断修改一个对象的属性来达到动画效果。关于Property Animator主要通过以下三个类来实现动画

    1.ValueAnimator

    .ObjectAnimator

    3.AnimatorSet

    主要介绍下ObjectAnimator的简单使用。嘎嘎。。就是简单点。。

    动画无非就是rotation(旋转),scale(缩放),tranlation(移动)。(还有前面提到的修改背景颜色)

    rotation(先看效果)


    动画代码如下:

    ObjectAnimator rotation = ObjectAnimator.ofFloat(View,"rotationY",0,360);//设置View从0度旋转360度
            rotation.setDuration(4000);//设置动画时长
            rotation.start();

    看源码,只看参数。

    public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
            。。。
        }
    第一个参数是动画作用的对象(以View为例,方便表达),第二个参数是View的一个“属性”名,第三个参数是一个长度可变的参数,它设置的是属性的值的变化,以上为例(0,360)指的就是”属性“从0变化到360。这里的属性为何打个引号是因为实际上他不是对象(View)的真正的属性(View里面没有rotationY属性),它实际上是通过我们前面传递进去的属性名(比如:rotationY)通过把第一个字母大写再到前面拼凑一个set(或者get)前缀得到:setRotationY(或者getRotationY)的方法名,最后通过反射调用此方法来修改(表达的不是很清楚,可以跟下源码)。这里说了第三个参数是一个可变长度的,那么我们如果设置为一个值也是可以的,但是我们的动画至少需要两个值!那么还有一个值哪来的类?我们设置的的这个值是动画开始值还是结束值类?我们还记得前面我们提到的设置属性名通过加前缀反射出方法名执行方法吗?如果我们只传递了一个值进去,那么我们第一个值(动画的开始值)就是通过getRotationY()方法获取,而动画结束值则是我们设置进入的值。

    其他的一些设置

    ObjectAnimator rotation = ObjectAnimator.ofFloat(mTxtTarget,"rotationY",0,360);
            rotation.setTarget(Object);//设置动画作用的对象
            rotation.setStartDelay(1000);//设置动画延时播放
            rotation.setInterpolator(new LinearInterpolator());//设置加速器 如果不设置则默认设置的是LinearInterpolator
            rotation.setEvaluator(new FloatEvaluator());//设置求职程序(不知道如何表达。。。) ObjectAnimator.ofFloat()默认设置的是FloatEvaluator,ObjectAnimator.ofInt()默认设置的是IntEvaluator,
            rotation.setRepeatCount(ObjectAnimator.INFINITE);//动画重复的次数 ObjectAnimator.INFINITE:无限循环
            rotation.setRepeatMode(ObjectAnimator.RESTART);//动画重复执行的顺序,有两个参数可选RESTART和REVERSE!以旋转360动画重复一次为例:
            // 1.RESTART:我们动画开始是从0度旋转到360度,重复一次时也是从0度旋转到360度-----》0---360    0----360
            // 2.REVERSE:动画从0度到360度,重复一次是从360度旋转到0度------》   0-----360------0
            rotation.setDuration(4000);//动画时长
            rotation.addListener() //添加一个动画监听,这里需要添加一个AnimatorListener对象,而AnimatorListener是一个接口,我们可以直接new AnimatorListener(){},
    // 但是这样的话我们就需要实现接口里面的全部方法,但是有时我们真正的项目中不需要去实现那么多方法,这样的话里面还有一个AnimatorListenerAdapter implements AnimatorListener,我们就只要
    //       new AnimatorListenerAdapter(){}这样你想监听哪个方法就监听哪个,代码更加简洁。
            rotation.start();//开始动画


    
    以上一些基本的设置都涉及到了,后面动画(Scale和Translation)基本就是修改属性名就好。
    

    Scale

    代码:

    ObjectAnimator scale = ObjectAnimator.ofFloat(mTxtTarget,"scaleX",0,2,1);
            scale.setDuration(4000);
            scale.setInterpolator(new AccelerateInterpolator());//加速度插值器:表示越到后面变化越快
            scale.start();
    Translation


    代码:

    ObjectAnimator translation = ObjectAnimator.ofFloat(mTxtTarget,"TranslationY",0,500,0);
            translation.setDuration(4000);
            translation.start();
    修改背景颜色


    ObjectAnimator translation = ObjectAnimator.ofInt(mTxtTarget,"backgroundColor",0xffff0000,0xff00ff00,0xff0000ff);
            translation.setEvaluator(new ArgbEvaluator());//起到一个颜色变化平缓过度效果
            translation.setDuration(4000);
            translation.start();
    关于颜色变化这里需要提到一点就是如果我们不设置ArgEvaluator那么颜色变化是一闪一闪的变化而不是平滑的效果!
    最后一个AnimatorSet,这里我就用xml来实现

    首先需要在res的animator文件夹下创建xml代码如下:

    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:ordering="sequentially">
        <set android:ordering="together">
            <objectAnimator
                android:duration="2000"
                android:propertyName="rotation"
                android:valueFrom="0"
                android:valueTo="360"
                android:valueType="floatType" />
            <objectAnimator
                android:duration="2000"
                android:propertyName="scaleX"
                android:valueFrom="0"
                android:valueTo="2" />
            <objectAnimator
                android:duration="2000"
                android:propertyName="scaleY"
                android:valueFrom="0"
                android:valueTo="2" />
            <objectAnimator
                android:duration="2000"
                android:propertyName="translationY"
                android:valueFrom="0"
                android:valueTo="400" />
    
        </set>
    
        <set android:ordering="together">
            <objectAnimator
                android:duration="2000"
                android:propertyName="rotation"
                android:valueFrom="360"
                android:valueTo="0" />
            <objectAnimator
                android:duration="2000"
                android:propertyName="translationY"
                android:valueFrom="400"
                android:valueTo="0" />
            <objectAnimator
                android:duration="2000"
                android:propertyName="scaleX"
                android:valueFrom="2"
                android:valueTo="1" />
            <objectAnimator
                android:duration="2000"
                android:propertyName="scaleY"
                android:valueFrom="2"
                android:valueTo="1" />
        </set>
    </set>
    然后再代码中引用
    AnimatorSet objectAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,R.animator.animator_set);//将xml中的动画加载
            ObjectAnimator translation = ObjectAnimator.ofInt(mTxtTarget,"backgroundColor",0xffff0000,0xff00ff00,0xff0000ff);
            translation.setEvaluator(new ArgbEvaluator());
            translation.setDuration(4000);
            translation.setInterpolator(new LinearInterpolator());
            objectAnimator.setTarget(mTxtTarget);
            objectAnimator.play(translation);
            objectAnimator.start();
    大家注意到了没?我并没有在xml中使用修改颜色backgroundColor的动画,因为在xml中无法设置Evaluator,如果在xml中做颜色变化的话那么颜色会一闪一闪的打不到我们需要的效果,在这里建议大家以后都用代码实现,你们看xml中一个objectAnimator只能做一件事(只有一个valueFrom(起点)和valueTo(终点))不能像代码中可以设置多个值。总之个人感觉代码实现起来方便直观,建议大家以后都用代码实现

    好了。。。说了只讲简单使用就好。哈哈。。这篇就记录这些吧。。。有理解错误的地方欢迎留言指正。。。。嘎嘎。。。

    我参考的资料

    1.《android群英转》--作者:徐宜生大大。。。

    2.启舰的博客专栏《Animation的动画详解》





    展开全文
  • Animator功能

    千次阅读 2018-01-02 16:31:54
    获取当前播放动画的名称 ...2.将Animator的动画倒着播放 将动画片段拖入animator中并添加一个float类型的参数为-1. 将该参数赋值到该动画片段的Multiplier参数中. 脚本中播放该动画 ActionCtl.animator.Pl
  • android animator

    万次阅读 2013-11-05 16:44:07
    3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中又引入了一个新的动画系统:property animation,这三种动画模式在SDK中被称为property animation,view animation,drawable ...
  • Animator

    2019-07-06 02:30:10
    1、Apply Root Motion AnimatorMoveScript.cs: void OnAnimatorMove() { //在脚本中使用此函数,并把它添加到相应对象上 ... //这个方法就会覆盖Apply Root Motion这个选项 ...如果勾选,动画将使用物理引擎,通常...
  • 属性动画之Animator API基本使用

    千次阅读 2019-05-16 17:21:23
    上一篇中我们简单的介绍了如何使用视图动画Animation API的基本使用,今天就来介绍一下功能更为强大的属性动画Animator API的基本使用,首先我们来看一看Animator的继承结构: Animator public abstract class ...
  • Unity/Animator -- 创建Animator Controller

    万次阅读 多人点赞 2017-09-09 14:13:08
    然而,通常情况下,一个单独的动画(即Animation Clip)可能无法很好地达到我们期望的效果,所以这时Animator Controller就能发挥其用武之地,帮助我们在合适的时间触发合适的动画,而不是在一个动画效果上无限循环。
  • 安卓之Animator讲解

    2020-04-25 10:33:51
    Animator Animator 动画的实现机制说起来其实更加简单一点,因为他其实只是计算动画开启之后,结束之前,到某个时间点得时候,某个属性应该有的值,然后通过回调接口去设置具体值,其实 Animator 内部并没有针对某个 view ...
  • 关于unity Animator 怎么判断一个动画播放结束这里有几种方法。希望对大家有帮助。还有其他办法的可以分享一下  第一种方法:在动画结束帧后面加个动画事件,调用下含这个变量的函数接口不是可以了?  第二种...
  • ulead gif animator 5.11是一款非常轻便的GIF制作工具,它的主要功能就是可以让用户将一系列的图片制作保存为GIF图片,比起那些非常专业的图片制作工具来说,这款软件最大的优势就是小巧和简单。软件界面制作精良...
  • ①Animation和Animator 虽然都是控制动画的播放,但是它们的用法和相关语法都是大有不同的。 Animation 控制一个动画的播放,而Animator是多个动画之间相互切换,并且Animator 有一个动画控制器,俗称动画状态机。 ...
  • Animator Override Controller 是一种资产类型,允许您扩展现有的Animator Controller,替换使用的特定动画,但保留原始结构,参数和逻辑。允许您创建相同基本状态机的多个变体,但每个变体使用不同的动画。 要...
  • Unity3d笔记——利用Animator使模型动起来一. 从Asset Store中下载合适的模型 本篇记录了一个小demo的制作过程,从找模型并下载,到创建Animator,绑定C#脚本,最后实现按下左箭头模型向我们的左边旋转,按下右箭头...
  • 新项目中使用了unity的动画控制器animator,写了以下几个小脚本。 1.导入fbx并拆分其中的动画,修改fbx导入设置。 2.导出fbx中的动画到指定目录,生成独立的Animation Clip. 3.动态创建及修改Animator Controller...
  • Unity3D控制Animator播放

    千次阅读 2018-05-18 20:27:38
    有两种实现方法:方法1:第一次打开界面开始播放特效动画,调用animator.Play("fx_name")方法,同时获取该动画的播放时长(如果自由一个播放面片的话:animator.GetCurrentAnimatorC...
  • AnimatorController复制 根据第一篇的自动创建AnimatorController,是将模型内的动画资源拿出全部放入控制器中,这样话省的一个个去拖拽。 但是有的时候这并不是我们想要的结果,我们有事需要替换摸个模型摸,但是...
  • Animator动画控制基础

    2018-11-20 21:58:03
    Animator动画控制基础 添加Animator Controller组件,选择相应动画状态机 状态机里新建空状态,建立连接 赋予状态Animation 状态转换的关键在于状态间的箭头→的Conditions ------------------------------------...
  • Animator 播放一个界面动画,如果动画是把界面从屏幕外面移动到屏幕内部的时候。打开界面就会显示界面的默认位置,然后播放Animator,然后Animator在会去在第一帧矫正界面的位置。就会出现一个问题,界面会先出现闪...
1 2 3 4 5 ... 20
收藏数 24,120
精华内容 9,648
关键字:

animator