Android应用开发之Android动画之属性动画深入分析
凌雪 2018-09-21 来源 :网络 阅读 547 评论 0

摘要:本文将带你了解Android应用开发之Android动画之属性动画深入分析,希望本文对大家学Android有所帮助。

本文将带你了解Android应用开发之Android动画之属性动画深入分析,希望本文对大家学Android有所帮助。


自从Android3.0 版本加入属性动画后,在平时的开发中多多少少也使用了,但是从来没有对其做一个系统的分析和总结,最近刚好有点时间,来对这个话题做一个分析和总结。
    概述
    动画对于GUI程序总是一个绕不过的话题,适当的动画可以使我们的人机交互界面更加的生动有趣,所以Android也提供了一套自己的动画体系。
Android动画体系
    至Android3.0以后(其实都不用再提这一点了,因为到目前为止,Android版本基本上已经收缩到4.0以上了),Android动画体系分为:
视图动画(View Animation)
    补间动画(Tween Animation) 逐帧动画(Frame   Animation) 属性动画(Property Animation)
    引入属性动画的原因
    其实这就是废话,肯定是View 动画存在一些问题,才引入新的解决方案。重点是View 动画存在哪些问题,而属性动画如何解决的这些问题,这个才是我们需要关注的重点,因为只有了解了一项技术的使用场景才能在日常的开发当中游刃有余的使用这些技术。
View 动画存在以下这些问题:
    只能对View对象做动画
    例如我们要对一个视图的背景颜色做动画,使用View 动画就无法完成 动画效果有限
    补间动画只有移动、旋转、缩放、变换透明度四种动画效果。 只改变了View的视觉效果,却没有真正改变View本身
    例如我们通过View平移动画将一个button从A位置移动到了B位置,当我们点击B位置上的按钮时候什么都不会发生,而点击A位置却触发了button的点击事件。
    属性动画就是为了解决以上问题而被引入的,但是View动画仍然有自己存在的价值,它仍然有适合自己的使用场景,而其运行效率以及使用复杂度都低于属性动画,所以还是应了那句老话,合适的就是最好的。我们开发中应该最求使用合适的技术来解决相应的问题。
    属性动画工作原理
    首先我们应该明白我们所说的动画是如何产生的。动画的本质可以理解为一系列连续图片(帧)快速不断的通过一个窗口展示在你的眼前。而人眼是有“视觉暂留”的,即人眼在某个视像消失后,仍可使该物像在视网膜上滞留0.1-0.4秒左右。这样当上一张图片的残像没有消失之前,下一张图片就有进入了你的眼睛,而这两张图片的变化的轻微的,所以你感觉动画丝滑细腻,无卡顿。
Android中的属性动画也逃不出这个套路。在一定时间间隔内(动画时长),通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。
    举个例子,Button 具有一个控制背景颜色的属性“backgroundColor”,那么属性动画就是通过不断的改变该属性的值,改变一次就相当于生成一幅图片展示在用户眼前,这样通过在一定的时间内以一定的频率不断重复这个动作,直到属性值达到了预先设置的值,动画结束。
    上一幅Carson_Ho博客(https://blog.csdn.net/carson_ho/article/details/72909894)的一张流程图,画的很清楚,就是有点糊。
   
    如何使用属性动画
    一项技术首先要会使用,然后才是深入理解其内部细节,进而用的更好!
    属性动画主要有三个类,Animator、 ValueAnimator、 ObjectAnimator,这三个类从右往左依次继承关系,那有点编程经验的同学应该知道哪个使用起来最简单了吧,没错就是ObjectAnimator,因为他最具体,最针对某一特定情形,那样它就被封装的最完善,当然代价就是会损失一定的灵活性。那我们先从简单的开始看。
ObjectAnimator使用方式
ObjectAnimator 有好多公共静态方法可以构造动画对象,如下图所示
   
    我们只介绍第一个,也是最简单最常用的一个,其他的思想类似,请自行研究。属性动画既可以像view动画那样以xml的形式构建,也可以使用java代码构建。然而在实际开发中我们更多的是使用java代码构建的方式,所以此处只介绍使用java代码实现属性动画的方法。
public static ObjectAnimator ofFloat(Object   target, String propertyName, float… values)
参数1:操作的对象,例如我接下来要操作一个Textview
参数2:操作的属性名称
    参数3:操作属性的变化路径值,例如只传入一个参数a,则表示此属性值从当前值变化到目 标值a;如果传入两个参数a,b,则表示此属性值从a变化到b;如果传入3个参数值a,b,c,则此属性值从a变化到b再变化到c,以此类推。
    当构建出ObjectAnimator 后就可以对其做一下自定义的配置了,例如,动画执行时长,动画延迟开始的时间,动画重复的策略,重复的次数等。
    正所谓:Talk is cheap, show me the code,来咱们看代码
?123ObjectAnimator tAni=   ObjectAnimator.ofFloat(tvOpr,"translationY",600);tAni.setDuration(2000);//设置动画时长2秒,如果不设置会采用系统默认值300tAni.start();
上面的代码在2秒的时间内将tvOpr-一个TextView从它当前的位置沿着Y轴移动到了600px的位置。如下图所示
   
    ?123ObjectAnimator rAni=   ObjectAnimator.ofFloat(tvOpr,"rotationY",0,360);rAni.setDuration(2000);rAni.start();
上面的代码在2秒的时间内将tvOpr-一个TextView绕Y轴从0旋转到360度。如下图所示
   
    我们还可以使用AnimatorSet类将不同的动画组合起来
AnimatorSet aSet=new AnimatorSet();
    ObjectAnimator tAni2=   ObjectAnimator.ofFloat(tvOpr,”translationY”,600);
    tAni2.setDuration(2000);
    ObjectAnimator rAni2=   ObjectAnimator.ofFloat(tvOpr,”rotationY”,0,360);
    rAni2.setDuration(2000);
    aSet.playTogether(
    tAni2,
    rAni2
    );
    aSet.setDuration(2000).start();
    上面的代码在2秒内完成了旋转和纵向平移的动画,如下图所示
   
    通过以上的示例代码相信大家已经知道如何使用ObjectAnimator 了,值得注意的是,我们要改变的那个属性必须有Set()和Get()方法,Get()方法在传入初始值的情况下可以省略,但是最好两个都有。如果要操作一个没这两个方法的对象怎么办呢,待深入分析的部分讲解。
ValueAnimator使用方式
ValueAnimator 本身并不作用于任何对象,其是对一个值进行动画,而我们可以监听到这个过程,然后在此过程中修改我们想要做动画的属性值。
ValueAnimator 有几个公共静态方法可以构造动画对象,如下图所示
   
    我们以ofInt 来做一个说明
public static ValueAnimator ofInt(int…   values)
    ?123456789101112131415161718192021222324252627282930private void   changeAlpha(){ // 步骤1:设置动画属性的变化路径 ValueAnimator anim   = ValueAnimator.ofInt(255,0, 255); // ofInt()作用有两个 // 1. 创建动画实例 // 2. 将传入的多个Int参数进行平滑过渡 //   如果传入了3个Int参数 a,b,c ,则是先从a平滑过渡到b,再从b平滑过渡到C,以此类推 //   ValueAnimator.ofInt()内置了整型估值器,直接采用默认的.不需要设置,即默认设置了如何从初始值过渡到结束值 // 步骤2:设置动画的播放各种属性 anim.setDuration(4000);// 设置动画运行的时长 anim.setStartDelay(0);// 设置动画延迟播放时间 anim.setRepeatCount(0);// 设置动画重复播放次数 = 重放次数+1    动画播放次数 = infinite时,动画无限重复 anim.setRepeatMode(ValueAnimator.REVERSE);//RESTART(默认):正序重放 REVERSE:倒序回放 anim.addUpdateListener(new   ValueAnimator.AnimatorUpdateListener() {  @Override  public   void onAnimationUpdate(ValueAnimator animation)  {int currentValue   = (Integer) animation.getAnimatedValue();//步骤3: 获得改变后的值0到255float fraction=animation.getAnimatedFraction();//当前动画进度System.out.println(currentValue);//   输出改变后的值// 步骤4:将改变后的值赋给对象的属性值tvOpr.getBackground().setAlpha(currentValue);// 步骤5:刷新视图,即重新绘制,从而实现动画效果tvOpr.requestLayout();  } }); anim.start();}
上面的代码在4秒的时间内将tvOpr-一个TextView背景透明度从完全不透明变到完全透明又变到完全不透明,如下图所示
   
    深入理解
    由于属性动画的工作原理比较简单,就是通过不断的调用要做动画的属性的Set()方法来改变此属性的值,进而造成了动画的效果。值得深入探讨的地方集中在如下两方面:
    动画是以何种方式进行的?例如是匀速的,还是加速的,还是按照贝塞尔曲线运动的,等等 动画进行到某一刻,那个属性的值是如何计算出来的?
    第一问题由属性动画中的插值器(Interpolator)来解决,第二个问题由估值器(TypeEvaluator)来解决 这两个很重要,正是由于他们的存在我们才可以实现非匀速的动画效果。TypeEvaluator 依赖Interpolator 工作,在TypeEvaluator的估值函数中,是需要Interpolator 的计算结果的。Android已经为我们内置几个插值器与估值器
    插值器:LinearInterpolator,AccelerateDecelerateInterpolator,DecelerateInterpolator,AccelerateInterpolator
估值器:IntEvaluator,FloatEvaluator,ArgbEvaluator
下面我们通过一个自定义二次贝塞尔曲线的View来说明插值器与估值器的使用方式,此View 出自chenwei.li的同学之手,我只是更正了其中的一些错误。冒着博客篇幅太长的风险,还是决定将完整代码贴出来。
public class BezierMoveView extends View   implements View.OnClickListener {  //开始点和结束点 private int mStartXPoint; private int   mStartYPoint; private int mEndXPoint; private int   mEndYPoint; //控制点 private   int mConXPoint; private int mConYPoint; //移动点 private int mMoveXPoint; private int   mMoveYPoint;  //路径和画笔 private Path mPath; private Paint   mPaint;  //圆形半径,画笔 private int mCircleRadius; private Paint   mCirlcePaint;   public BezierMoveView(Context context)   {  super(context);  init(context); }   public   BezierMoveView(Context context, AttributeSet attrs)   {  super(context,   attrs);  init(context); }  public   BezierMoveView(Context context, AttributeSet attrs, int defStyleAttr)   {  super(context, attrs,   defStyleAttr);  init(context); }  /**  * 进行初始化的一些操作  */ private   void init(Context context) {   //设置各点的位置  mStartXPoint = 100;  mStartYPoint =   100;  mEndXPoint = 600;  mEndYPoint =   600;  mConXPoint = 400;  mConYPoint =   0;  mMoveXPoint = 100;  mMoveYPoint =   100;   //路径,画笔设置  mPath   = new Path();  mPaint = new   Paint(Paint.ANTI_ALIAS_FLAG);  mPaint.setColor(Color.BLUE);  mPaint.setStyle(Paint.Style.STROKE);  mPaint.setStrokeWidth(5);   mCircleRadius   = 20;  mCirlcePaint = new   Paint(Paint.ANTI_ALIAS_FLAG);  mCirlcePaint.setColor(Color.BLUE);  mCirlcePaint.setStyle(Paint.Style.FILL);   setOnClickListener(this); }  @Override protected   void onDraw(Canvas canvas)   {  super.onDraw(canvas);  mPath.reset();  //贝塞尔曲线  mPath.moveTo(mStartXPoint,   mStartYPoint);  mPath.quadTo(mConXPoint, mConYPoint, mEndXPoint,   mEndYPoint);  canvas.drawPath(mPath, mPaint);   //画圆  canvas.drawCircle(mStartXPoint,   mStartYPoint, mCircleRadius,   mCirlcePaint);  canvas.drawCircle(mMoveXPoint, mMoveYPoint,   mCircleRadius, mCirlcePaint);  canvas.drawCircle(mEndXPoint,   mEndYPoint, mCircleRadius, mCirlcePaint); }  @Override public   void onClick(View v) {  ValueAnimator valueAnimator =   ValueAnimator.ofObject(new CirclePointEvaluator(), new Point(mStartXPoint,   mStartYPoint), new Point(mEndXPoint, mEndYPoint));  valueAnimator.setDuration(2000);  valueAnimator.setInterpolator(new    AccelerateDecelerateInterpolator());  valueAnimator.addUpdateListener(new   ValueAnimator.AnimatorUpdateListener() {@Overridepublic void   onAnimationUpdate(ValueAnimator animation) { Point point = (Point)   animation.getAnimatedValue(); mMoveXPoint = point.x; mMoveYPoint =   point.y; invalidate();}  });  valueAnimator.start(); }  public   class CirclePointEvaluator implements TypeEvaluator   {  @Override  public Object evaluate(float t, Object   startValue, Object endValue) { Point startPoint = (Point)   startValue;Point endPoint = (Point) endValue; float temp = 1 -   t; int x = (int) (temp * temp * startPoint.x + 2 * t * temp * mConXPoint   + t * t * endPoint.x);int y = (int) (temp * temp * startPoint.y + 2 * t *   temp * mConYPoint + t * t * endPoint.y); return new   Point(x,y);  } }}
重点看代码靠下位置的onClick 和下面的自定义估值器代码。当我们点击这个view的时候,会触发一个动画,这个动画使用如下方法设置插值器,此处为加速减速插值器
valueAnimator.setInterpolator(new   AccelerateDecelerateInterpolator());,而且设置了自定义的CirclePointEvaluator 估值器,动画每执行一帧,监听就会被回调,然后在回调方法onAnimationUpdate()中获得了当前的属性值,更改小球的位置坐标,然后触发重绘。
   
    所以使用ValueAnimator 可以做出非常绚烂的效果。
    注意事项
    如果动画设置为无限循环,在退出Activity时注意销毁,防止内存泄漏。    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注移动开发之Android频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程