热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

属性动画和补间动画的原理及区别

首先来复习下属性动画相关知识:Android属性动画(一)——属性动画的基本使用_Marck的博客-CSDN博客_android属性动画

首先来复习下属性动画相关知识:

Android属性动画(一)—— 属性动画的基本使用_Marck的博客-CSDN博客_android 属性动画

Android属性动画(二)—— 插值器和估值器_Marck的博客-CSDN博客_属性动画插值器估值器 


一、结论:

属性动画:所见即所得,最终的显示位置变了,坐标也变了;
补间动画:所见并非所得,虽然最终的显示位置变了,但是坐标还是之前的。

每日一问:谈谈属性动画和补间动画的原理及区别 - 简书


二、属性动画和补间动画的基本编写方式

我一度在论坛上看到人使用了 TranslateAnimation 对控件做了移动操作,然后发现在 View 的新位置点击并没有响应自己的点击事件,反倒是之前的位置能够响应。实际上,补间动画仅仅是对 View 在视觉效果上做了移动、缩放、旋转和淡入淡出的效果,其实并没有真正改变 View 的属性。但我们大多数情况下肯定希望 View 在经过动效后响应触摸事件的位置和视觉效果相同,所以在 Android 3.0 之后引入了属性动画,彻底解决了这个难题。

可能还有一些小伙伴不明白怎样的代码是属性动画,怎样的代码是补间动画。下面针对 View 向右平移 500 px 做一下简单的演示。

对于属性动画,你可以用下面的两种方式。

ObjectAnimator.ofFloat(tv1, "translationX", 0f, 500f).setDuration(1000).start()
// 或者像这样
tv1.animate().setDuration(1000).translationX(500f)

但用补间动画,并且你想达到同样的效果的话。

val anim = TranslateAnimation(0f, 500f, 0f, 0f)
anim.duration = 1000
anim.fillAfter = true // 设置保留动画后的状态
tv1.startAnimation(anim)

  • 属性动画的使用注意点

对于属性动画来说,尤其需要注意的是操作的属性需要有 set 和 get 方法,不然你的 ObjectAnimator 操作就不会生效。比如水平平移,我们知道,View 的 translationX 属性设置方法接受的是 float 值,所以你把上面的操作编写为 ofInt 就不会生效,比如:

ObjectAnimator.ofInt(tv1, "translationX", 0, 500).setDuration(1000).start()

对于我们需要用到但又没有写好的属性,比如我们自定义一个进度条 View,我们需要实时展示进度,这时候我们就可以自己定义一个属性,并让它支持 set 和 get,那么在外面就可以对这个自定义的 View 做属性动画操作了。


属性动画和补间动画工作原理

属性动画

属性动画的工作原理很简单,其实就是在一定的时间间隔内,通过不断地对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在属性上的动画效果。


这个属性可以是任意对象的属性。


从上述工作原理可以看出属性动画有两个非常重要的类:ValueAnimator 类 & ObjectAnimator 类,二者的区别在于:
ValueAnimator 类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作;而 ValueAnimator 类本质上是一种 改变值 的操作机制。

ObjectAnimator 类是先改变值,然后 自动赋值 给对象的属性从而实现动画;是 直接 对对象属性进行操作;可以理解为:ObjectAnimator 更加智能、自动化程度更高。

补间动画

而对于补间动画,我们不妨跟进源码,看看到底做了什么操作。

/*** Start the specified animation now.** @param animation the animation to start now*/
public void startAnimation(Animation animation) {animation.setStartTime(Animation.START_ON_FIRST_FRAME);setAnimation(animation);invalidateParentCaches();invalidate(true);
}

看到了非常明显 invalidate() 方法,很明显,补间动画在执行的时候,直接导致了 View 执行 onDraw() 方法。总的来说,补间动画的核心本质就是在一定的持续时间内,不断改变 Matrix 变换,并且不断刷新的过程。


为什么属性动画移动一个 View 后,目标位置还可以响应触摸事件呢?

这个问题来自 wanandroid,在此前,我一直认为既然 View 的属性得到了改变,那么经过属性动画后的控件应该所有属性都等同于直接设置在动画后的位置的控件。

看完「陈小缘」的回答后,我突然才想到,虽然 View 做了属性上的改变,但其实并没有更改 Viewleftrighttopbottom 这些属性,而这些属性恰恰决定了 ViewGroup 的触摸区域判断。


  • tv1.animate().setDuration(1000).translationX(500f)

那么,假定我们的 View 经过了上面的平移操作后,为什么点击新的位置能够响应到这个点击事件呢?

看了「陈小缘」的回答,我顺便深入了一波源码,想想必须在这分享给大家。

我们知道,在 ViewGroup 没有重写 onInterceptTouchEvent() 方法进行事件拦截的时候,我们一定会通过其 dispatchTouchEvent() 方法进行事件分发,而决定我们哪一个子 View 响应我们的触摸事件的条件又是 我们手指的位置必须在这个子 View 的边界范围内,也就是 leftrighttopbottom 这四个属性形成的矩形区域。

那么,如果我们的 View 已经进行了属性动画后,现在手指响应的触摸位置区域肯定不是 View 自己的leftrighttopbottom 这四个属性形成的区域了,但这个 View 却神奇的响应了我们的点击事件。

/*** Returns a MotionEvent that's been transformed into the child's local coordinates.** It's the responsibility of the caller to recycle it once they're finished with it.* @param event The event to transform.* @param child The view whose coordinate space is to be used.* @return A copy of the the given MotionEvent, transformed into the given View's coordinate* space.*/
private MotionEvent getTransformedMotionEvent(MotionEvent event, View child) {final float offsetX = mScrollX - child.mLeft;final float offsetY = mScrollY - child.mTop;final MotionEvent transformedEvent = MotionEvent.obtain(event);transformedEvent.offsetLocation(offsetX, offsetY);if (!child.hasIdentityMatrix()) {transformedEvent.transform(child.getInverseMatrix());}return transformedEvent;
}/*** Returns true if the transform matrix is the identity matrix.* Recomputes the matrix if necessary.** @return True if the transform matrix is the identity matrix, false otherwise.*/
final boolean hasIdentityMatrix() {return mRenderNode.hasIdentityMatrix();
}/*** Utility method to retrieve the inverse of the current mMatrix property.* We cache the matrix to avoid recalculating it when transform properties* have not changed.** @return The inverse of the current matrix of this view.* @hide*/
public final Matrix getInverseMatrix() {ensureTransformationInfo();if (mTransformationInfo.mInverseMatrix == null) {mTransformationInfo.mInverseMatrix = new Matrix();}final Matrix matrix = mTransformationInfo.mInverseMatrix;mRenderNode.getInverseMatrix(matrix);return matrix;
}

原来,ViewGroupgetTransformedMotionEvent() 方法中会通过子 ViewhasIdentityMatrix() 方法来判断子 View 是否应用过位移、缩放、旋转之类的属性动画。如果应用过的话,那还会调用子 ViewgetInverseMatrix() 做「反平移」操作,然后再去判断处理后的触摸点是否在子 View 的边界范围内。

补间动画和属性动画改变的是同一个 Matrix 么?

负责任的说:肯定不是。

属性动画所影响的 Matrix,是在 ViewmRenderNode 中的 stagingProperties 里面的,这个 Matrix 在每个 View 之间都是独立的,所以可以各自保存不同的变换状态。

而补间动画所操作的 Matrix,其实是借用了它父容器的一个叫 mChildTransformation 的属性 ( 里面有 Matrix ),通过 getChildTransformation 获得。
也就是说,一个 ViewGroup 中,无论它有几个子 View 都好,在这些子 View 播放补间动画的时候,都是共用同一个 Transformation 对象的(也就是共用一个 Matrix ),这个对象放在 ViewGroup 里面。

有同学可能会问:共用?不可能吧,那为什么可以同时播放好几个动画,而互相不受影响呢?
是的,在补间动画更新每一帧的时候,父容器的 mChildTransformation 里面的 Matrix,都会被 reset()

每次重置 Matrix 而不受影响的原因:
是因为这些补间动画,都是基于当前播放进度,来计算出绝对的动画值并应用的,保存旧动画值是没有意义的。
就拿位移动画 TranslateAnimation 来说,比如它要向右移动 500,当前的播放进度是 50%,那就是已经向右移动了 250,在 View 更新帧的时候,就会把这个向右移动了250的 Matrix 应用到 Canvas 上,当下次更新帧时,比如进度是 60%,那计算出来的偏移量就是 300,这时候,已经不需要上一次的旧值 250 了,就算 Matrix 在应用前被重置了,也不影响最后的效果。

感叹,今天又发现了一些非常通用却被我们忽略掉的东西,不得不说,鸿洋的 wanandroid 带给了我们很多东西,更加惊叹的是「陈小缘」同学的 View 相关功底确实很强,这也难怪,他能写出如何有逼格的自定义 View 了。

View 相关的非常渴望了解的可以到小缘的博客去一探究竟。 https://me.csdn.net/u011387817


推荐阅读
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Android日历提醒软件开源项目分享及使用教程
    本文介绍了一款名为Android日历提醒软件的开源项目,作者分享了该项目的代码和使用教程,并提供了GitHub项目地址。文章详细介绍了该软件的主界面风格、日程信息的分类查看功能,以及添加日程提醒和查看详情的界面。同时,作者还提醒了读者在使用过程中可能遇到的Android6.0权限问题,并提供了解决方法。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • DSP中cmd文件的命令文件组成及其作用
    本文介绍了DSP中cmd文件的命令文件的组成和作用,包括链接器配置文件的存放链接器配置信息、命令文件的组成、MEMORY和SECTIONS两个伪指令的使用、CMD分配ROM和RAM空间的目的以及MEMORY指定芯片的ROM和RAM大小和划分区间的方法。同时强调了根据不同芯片进行修改的必要性,以适应不同芯片的存储用户程序的需求。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • Python中sys模块的功能及用法详解
    本文详细介绍了Python中sys模块的功能及用法,包括对解释器参数和功能的访问、命令行参数列表、字节顺序指示符、编译模块名称等。同时还介绍了sys模块中的新功能和call_tracing函数的用法。推荐学习《Python教程》以深入了解。 ... [详细]
author-avatar
破晓sxy
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有