我发现了很多建议使用Timeline与KeyFrame.onFinished在计划任务的JavaFX。但是AnimationTimer文档说,它的handle方法每秒被调用 60 次。
从文档中不清楚,但似乎在内部Timeline使用AnimationTimer。这是否意味着调度的时间线解决方案强加了 CPU 密集型轮询模式?如果这实际上是 JavaFX 的工作方式,那么推荐其他哪些调度方法?
JavaFX 中调度的建议使用规则
鉴于时间轴的工作原理(见下文),可以推导出一些基本规则:
当您想要安排修改场景图属性的快速操作时,请使用时间轴。
当您想要执行以下任何操作时,请勿使用时间轴:
此外,对于在固定延迟后更新场景图的一次性执行,我喜欢使用PauseTransition。
对特定问题和疑虑的回答
从文档中不清楚,但时间线似乎在内部使用 AnimationTimer。
不,时间轴在内部不使用AnimationTimer。但是,时间线仍然是基于脉冲的,并且将接收代码以更新时间线的当前状态,将在每个脉冲上调用(通常每秒 60 次)。要了解什么是脉冲,请阅读下面的背景部分。
这是否意味着调度的时间线解决方案强加了 CPU 密集型轮询模式?
是的,有一些开销,但它可能很小,可以忽略不计。不管您是否自己创建时间线,JavaFX 系统都会在每个脉冲上运行一些工作。除非您对对象进行大量动画处理或对每个脉冲进行大量工作,否则处理每个脉冲的时间将是微不足道的,因为对每个脉冲要做的工作很少。
如果这实际上是 JavaFX 的工作方式,那么推荐其他哪些调度方法?
Timeline 有许多替代方案:
如果您确实使用时间线以外的其他内容,则需要注意:
背景信息
时间线操作
例如,假设您有一个时间轴,它有一个持续时间为 1 秒的关键帧。内部时间线仍会在每个脉冲中更新。这是必需的,因为时间轴不仅仅是一个调度程序,它还具有其他属性和任务。例如,时间轴有一个currentTime属性。currentTime 将在每个脉冲中更新,以便它始终准确地反映当前时间。类似地,时间线的内部逻辑将检查是否有任何与时间线关联的KeyValues,并根据其关联的Interpolator在每个脉冲上更新它们的值. 内部逻辑将检查每个脉冲以查看 Timeline 是否已完成,以调用 onFinished 处理程序。它还将评估当前时间与每个关键帧的持续时间,如果匹配,则为关键帧触发适当的动作事件。
因此,时间线就像一个调度程序,因为它可以在特定时间执行关键帧,但它不仅仅是一个调度程序,因为它还维护它的当前时间、当前状态和不断改变相关键值的能力。此外,您可以改变时间线的速率、方向和周期数,以及将其暂停到位,然后再恢复,这也不同于传统的调度程序。
Timeline 的一个方面是因为它基于 JavaFX 系统中脉冲的回调;一切都在 JavaFX 应用程序线程上运行。因此,当您使用时间线(甚至 1000 个)时,不会产生额外的线程,因此从这个角度来看,它是轻量级的。此外,时间线调用的所有逻辑都发生在 JavaFX 应用程序线程上。因为一切都在 JavaFX 应用程序线程上运行,这使得时间轴非常适合操作活动 JavaFX 场景图的元素和属性(此类操作必须在 JavaFX 应用程序线程上执行)。但是,如果您想在相关的时间轴关键帧中执行大量耗时的 CPU 工作或阻塞 I/O,那么时间轴将是一个糟糕的选择。这是因为 JavaFX 应用程序线程将在该工作发生时被阻塞,从而冻结您的 UI。
脉冲
JavaFX 运行时系统基于脉冲。
脉冲是一个事件,它向 JavaFX 场景图指示是时候将场景图上元素的状态与 Prism 同步了。脉冲以每秒 60 帧 (fps) 的最大速度进行节流,并在场景图上运行动画时触发。即使动画没有运行,当场景图中的某些内容发生更改时,也会安排一个脉冲。例如,如果按钮的位置发生变化,则安排一个脉冲。
当脉冲被触发时,场景图上元素的状态会同步到渲染层。脉冲使应用程序开发人员能够异步处理事件。这一重要功能允许系统在脉冲上批处理和执行事件。
JavaFX 动画系统存储所有动画和动画计时器的列表,这些动画和动画计时器已创建,但未被垃圾回收。在每个脉冲上,它将遍历此列表。每个动画计时器都会在每个脉冲上调用它的句柄回调。每个时间线或过渡将类似地更新其内部状态,并根据需要更新任何关联的关键值或触发关键帧动作事件。