热门标签 | 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


推荐阅读
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • Android 自定义 RecycleView 左滑上下分层示例代码
    为了满足项目需求,需要在多个场景中实现左滑删除功能,并且后续可能在列表项中增加其他功能。虽然网络上有很多左滑删除的示例,但大多数封装不够完善。因此,我们尝试自己封装一个更加灵活和通用的解决方案。 ... [详细]
  • 本文详细介绍了在 CentOS 7 系统中配置 fstab 文件以实现开机自动挂载 NFS 共享目录的方法,并解决了常见的配置失败问题。 ... [详细]
  • 本文介绍如何在 Android 中自定义加载对话框 CustomProgressDialog,包括自定义 View 类和 XML 布局文件的详细步骤。 ... [详细]
  • 原文网址:https:www.cnblogs.comysoceanp7476379.html目录1、AOP什么?2、需求3、解决办法1:使用静态代理4 ... [详细]
  • 开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤
    开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • 本文深入探讨了Java多线程环境下的同步机制及其应用,重点介绍了`synchronized`关键字的使用方法和原理。`synchronized`关键字主要用于确保多个线程在访问共享资源时的互斥性和原子性。通过具体示例,如在一个类中使用`synchronized`修饰方法,展示了如何实现线程安全的代码块。此外,文章还讨论了`ReentrantLock`等其他同步工具的优缺点,并提供了实际应用场景中的最佳实践。 ... [详细]
  • 在Android开发中,实现多点触控功能需要使用`OnTouchListener`监听器来捕获触摸事件,并在`onTouch`方法中进行详细的事件处理。为了优化多点触控的交互体验,开发者可以通过识别不同的触摸手势(如缩放、旋转等)并进行相应的逻辑处理。此外,还可以结合`MotionEvent`类提供的方法,如`getPointerCount()`和`getPointerId()`,来精确控制每个触点的行为,从而提升用户操作的流畅性和响应性。 ... [详细]
  • 掌握Android UI设计:利用ZoomControls实现图片缩放功能
    本文介绍了如何在Android应用中通过使用ZoomControls组件来实现图片的缩放功能。ZoomControls提供了一种简单且直观的方式,让用户可以通过点击放大和缩小按钮来调整图片的显示大小。文章详细讲解了ZoomControls的基本用法、布局设置以及与ImageView的结合使用方法,适合初学者快速掌握Android UI设计中的这一重要功能。 ... [详细]
  • 技术分享:深入解析GestureDetector手势识别机制
    技术分享:深入解析GestureDetector手势识别机制 ... [详细]
  • 开发笔记:深入解析Android自定义控件——Button的72种变形技巧
    开发笔记:深入解析Android自定义控件——Button的72种变形技巧 ... [详细]
  • 本文深入探讨了 Android DrawingView 的优化技巧与实现方法,重点介绍了如何实现平滑绘制效果。通过支持常见的绘图工具和形状,以及图层变换功能,提升了用户体验。文章详细解析了绘制过程中的性能优化策略,包括减少重绘次数、使用硬件加速和优化内存管理等技术,为开发者提供了实用的参考。 ... [详细]
  • 本文对比了杜甫《喜晴》的两种英文翻译版本:a. Pleased with Sunny Weather 和 b. Rejoicing in Clearing Weather。a 版由 alexcwlin 翻译并经 Adam Lam 编辑,b 版则由哈佛大学的宇文所安教授 (Prof. Stephen Owen) 翻译。 ... [详细]
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社区 版权所有