布局更改时的动画

2023-12-24

每次调整窗口大小时,JavaFX 中的基本 FlowPane 都会对其中的项目进行布局。然而,没有动画,结果相当不和谐。

我在 FlowPane 内每个节点的layoutX 和layoutY 属性上连接了一个更改侦听器,结果或多或少有效,但有时当我快速调整窗口大小时,元素会留在不一致的位置。

我缺少什么?

package javafxapplication1;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.animation.Transition;
import javafx.animation.TranslateTransition;
import javafx.beans.property.DoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.util.Duration;

/**
 * Animates an object when its position is changed. For instance, when
 * additional items are added to a Region, and the layout has changed, then the
 * layout animator makes the transition by sliding each item into its final
 * place.
 */
public class LayoutAnimator implements ChangeListener, ListChangeListener<Node> {

  private Map<Node, Transition> nodesInTransition;

  public LayoutAnimator() {
    this.nodesInTransition = new HashMap<>();
  }

  /**
   * Animates all the children of a Region.
   * <code>
   *   VBox myVbox = new VBox();
   *   LayoutAnimator animator = new LayoutAnimator();
   *   animator.observe(myVbox.getChildren());
   * </code>
   *
   * @param nodes
   */
  public void observe(ObservableList<Node> nodes) {
    for (Node node : nodes) {
      this.observe(node);
    }
    nodes.addListener(this);
  }

  public void unobserve(ObservableList<Node> nodes) {
    nodes.removeListener(this);
  }

  public void observe(Node n) {
    n.layoutXProperty().addListener(this);
    n.layoutYProperty().addListener(this);
  }

  public void unobserve(Node n) {
    n.layoutXProperty().removeListener(this);
    n.layoutYProperty().removeListener(this);
  }

  @Override
  public void changed(ObservableValue ov, Object oldValue, Object newValue) {
    final Double oldValueDouble = (Double) oldValue;
    final Double newValueDouble = (Double) newValue;
    final Double changeValueDouble = newValueDouble - oldValueDouble;
    DoubleProperty doubleProperty = (DoubleProperty) ov;

    Node node = (Node) doubleProperty.getBean();
    final TranslateTransition t;
    if ((TranslateTransition) nodesInTransition.get(node) == null) {
      t = new TranslateTransition(Duration.millis(150), node);
    } else {
      t = (TranslateTransition) nodesInTransition.get(node);
    }

    if (doubleProperty.getName().equals("layoutX")) {
      Double orig = node.getTranslateX();
      if (Double.compare(t.getFromX(), Double.NaN) == 0) {
        t.setFromX(orig - changeValueDouble);
        t.setToX(orig);
      }
    }
    if (doubleProperty.getName().equals("layoutY")) {
      Double orig = node.getTranslateY();
      if (Double.compare(t.getFromY(), Double.NaN) == 0) {
        t.setFromY(orig - changeValueDouble);
        t.setToY(orig);
      }
    }
    t.play();

  }

  @Override
  public void onChanged(ListChangeListener.Change change) {
    while (change.next()) {
      if (change.wasAdded()) {
        for (Node node : (List<Node>) change.getAddedSubList()) {
          this.observe(node);
        }
      } else if (change.wasRemoved()) {
        for (Node node : (List<Node>) change.getRemoved()) {
          this.unobserve(node);
        }
      }
    }
  }
}

为了便于阅读,此处提供了一个要点,并附带一个测试用例:https://gist.github.com/teyc/5668517 https://gist.github.com/teyc/5668517


这真是个好主意。我喜欢这个。您应该考虑将新的布局管理器贡献给jfxtras http://jfxtras.org.

为什么您最初发布的解决方案不起作用

您的问题是尝试记录 translateX/Y 值的原始值并以该原始值结束翻译转换的逻辑。

当 TranslateTransition 正在进行时,它会修改 translateX/Y 值。使用当前代码,如果您在动画完成之前快速调整屏幕大小,则可以将 TranslateTransition 的 toX/Y 属性设置为当前的 translateX/Y 值。这使得动画的最终静止位置成为某个先前的中间点,而不是节点所需的最终静止位置(所需的位置只是节点的 translateX/Y 值为 0 的点)。

如何修复它

解决方法很简单 - TranslateTransition 的 toX/Y 应始终为零,因此转换最终总是将节点翻译到当前布局位置应有的位置,且没有翻译增量。

示例解决方案代码片段

这是核心更新的转换代码:

TranslateTransition t;
switch (doubleProperty.getName()) {
  case  "layoutX":
    t = nodeXTransitions.get(node);
    if (t == null) {
      t = new TranslateTransition(Duration.millis(150), node);
      t.setToX(0);
      nodeXTransitions.put(node, t);
    }
    t.setFromX(node.getTranslateX() - delta);
    node.setTranslateX(node.getTranslateX() - delta);
    break;

  default: // "layoutY"
    t = nodeYTransitions.get(node);
    if (t == null) {
      t = new TranslateTransition(Duration.millis(150), node);
      t.setToY(0);
      nodeYTransitions.put(node, t);
    }
    t.setFromY(node.getTranslateY() - delta);
    node.setTranslateY(node.getTranslateY() - delta);
}

t.playFromStart();

示例解决方案输出

添加更多矩形并调整屏幕大小以强制使用新布局后,更新后的动画制作器的输出是(使用您在要点上提供的测试工具):

关于调试动画并修复示例代码的其他问题

在调试动画时,我发现放慢动画速度会更容易。为了弄清楚如何修复发布的程序,我将 TranslateTransition 设置为一秒,以便让眼睛有足够的时间来查看实际发生的情况。

放慢动画速度帮助我看到,监听器生成的实际动画似乎不会发生,直到节点重新布局后的一帧,从而产生一个短暂的故障,节点将暂时出现在其目标位置,然后继续回到原来的位置,然后慢慢动画到目标位置。

初始定位故障的修复方法是在开始播放动画之前将侦听器中的节点平移回原始位置。

您的代码使用nodesInTransition 映射来跟踪当前转换的节点,但它不会在映射中放置任何内容。我将其重命名为nodeXTransitions 和nodeYTransitions(因为我使用单独的x 和y 转换,而不是对两者使用单个转换,因为使用单独的转换似乎更容易)。我在创建节点转换时将其放入地图中,以便在创建新转换时可以停止旧转换。这似乎并不是绝对必要的,因为如果没有地图逻辑,一切似乎都可以正常工作(也许 JavaFX 已经在其动画框架内隐式地做了类似的事情),但这似乎是一个安全的事情,所以我保留了它。

在我进行了上面详细说明的更改后,一切似乎都正常。

潜在的进一步改进

也许可以对动画的计时进行一些改进,这样如果动画部分播放并且发生重新布局,您可能不想从头开始播放整个动画以进行新的重新布局。或者,您可能希望所有动画节点以恒定、相同的速度移动,而不是持续一段时间。但从视觉上看,这似乎并不重要,所以我不会真正担心它。

测试系统

我在 Java8b91 和 OS X 10.8 上进行了测试。

更新解决方案的完整代码

更新的布局动画器的完整代码是:

import javafx.animation.TranslateTransition;
import javafx.beans.property.DoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.util.Duration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Animates an object when its position is changed. For instance, when
 * additional items are added to a Region, and the layout has changed, then the
 * layout animator makes the transition by sliding each item into its final
 * place.
 */
public class LayoutAnimator implements ChangeListener<Number>, ListChangeListener<Node> {

  private Map<Node, TranslateTransition> nodeXTransitions = new HashMap<>();
  private Map<Node, TranslateTransition> nodeYTransitions = new HashMap<>();

  /**
   * Animates all the children of a Region.
   * <code>
   *   VBox myVbox = new VBox();
   *   LayoutAnimator animator = new LayoutAnimator();
   *   animator.observe(myVbox.getChildren());
   * </code>
   *
   * @param nodes
   */
  public void observe(ObservableList<Node> nodes) {
    for (Node node : nodes) {
      this.observe(node);
    }
    nodes.addListener(this);
  }

  public void unobserve(ObservableList<Node> nodes) {
    nodes.removeListener(this);
  }

  public void observe(Node n) {
    n.layoutXProperty().addListener(this);
    n.layoutYProperty().addListener(this);
  }

  public void unobserve(Node n) {
    n.layoutXProperty().removeListener(this);
    n.layoutYProperty().removeListener(this);
  }

  @Override
  public void changed(ObservableValue<? extends Number> ov, Number oldValue, Number newValue) {
    final double delta = newValue.doubleValue() - oldValue.doubleValue();
    final DoubleProperty doubleProperty = (DoubleProperty) ov;
    final Node node = (Node) doubleProperty.getBean();

    TranslateTransition t;
    switch (doubleProperty.getName()) {
      case  "layoutX":
        t = nodeXTransitions.get(node);
        if (t == null) {
          t = new TranslateTransition(Duration.millis(150), node);
          t.setToX(0);
          nodeXTransitions.put(node, t);
        }
        t.setFromX(node.getTranslateX() - delta);
        node.setTranslateX(node.getTranslateX() - delta);
        break;

      default: // "layoutY"
        t = nodeYTransitions.get(node);
        if (t == null) {
          t = new TranslateTransition(Duration.millis(150), node);
          t.setToY(0);
          nodeYTransitions.put(node, t);
        }
        t.setFromY(node.getTranslateY() - delta);
        node.setTranslateY(node.getTranslateY() - delta);
    }

    t.playFromStart();
  }

  @Override
  public void onChanged(Change change) {
    while (change.next()) {
      if (change.wasAdded()) {
        for (Node node : (List<Node>) change.getAddedSubList()) {
          this.observe(node);
        }
      } else if (change.wasRemoved()) {
        for (Node node : (List<Node>) change.getRemoved()) {
          this.unobserve(node);
        }
      }
    }
  }
}

关于独立于用户 TranslateX/Y 设置进行布局动画更改

当前提出的解决方案的一个缺点是,如果自定义布局管理器的用户将translateX/Y设置直接应用于布局节点,那么当布局管理器转发所有内容时,他们将丢失这些值(作为translateX) /Y 最终被设置回 0)。

为了保留用户的translateX/Y,可以更新解决方案以使用自定义过渡而不是平移过渡。自定义转换可以应用于翻译 http://docs.oracle.com/javafx/2/api/javafx/scene/transform/Translate.html到节点的转换 http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#getTransforms%28%29。然后,只有每个节点上的附加变换会受到布局动画的内部工作方式的影响 - 用户的原始 translateX/Y 值不会受到影响。

我从你的问题中分出了要点来实现上面提到的自定义转换增强 https://gist.github.com/jewelsea/5683558.


如果你有兴趣的话,你可以看看openjfx代码 https://wiki.openjdk.java.net/display/OpenJFX/Main对于选项卡标题、TitledPanes 和 Accordions,并查看这些控件的外观如何处理其子节点的布局更改动画。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

布局更改时的动画 的相关文章

  • 通过 HTTPS 的 JavaFX.MediaPlayer?

    我尝试使用 MediaPlayer 播放定义了 HTTPS url 的资源 但它显示协议不受支持 在 API 参考中 他们声明 FILE HTTP JAR 是受支持的协议 未提及 HTTPS 是否可以通过 HTTPS 使用 url 在我看来
  • JavaFX 中的可复制标签/文本字段/LabeledText

    我只想在 JavaFX 中创建可复制的标签 我尝试创建没有背景 没有焦点边框和默认背景颜色的 TextField 但没有成功 我发现了很多关于如何从控制中删除焦点背景的问题 但所有这些看起来都像是 黑客 是否有任何标准解决方案来实现可复制文
  • 无法在 JavaFX TableView 单元格值上显示工具提示

    In my JavaFX TableView我有一个TableColumn我已将 Cell Factory 设置为渲染ProgressBar以及对于其他TableColumns 我已将 Cell Factory 设置为显示ToolTip 就
  • 在 JavaFx 标签中显示变化的值

    在JavaFX中 如何使用 标签 显示随时间不断变化的值 有很多方法可以实现这一点 最方便的是使用 JavaFX 的 DataBinding 机制 assuming you have defined a StringProperty cal
  • TriangleMesh JavaFX 中 getNormals() 方法的用途是什么

    我目前正在开发 JavaFX 3D 应用程序 并在 TriangleMesh 类中遇到 getNormals 方法 正如 Triangle Mesh 类用于创建用户定义的 Java FX 3D 对象一样 其中getPoints 用于添加Po
  • JavaFX 2 ComboBox setValue() 不设置 CB 文本

    我的问题是 使用 setValue 选择后 所选的 ComboBox 项目文本在屏幕上不可见 以下是一些细节 将项目添加到我的 CB combo getItems add a combo getItems add b combo getIt
  • 如何更改JavaFX中的突出显示颜色?

    如何更改JavaFX中的突出显示颜色 通常它是带有渐变的蓝色 但我想要绿色突出显示 我已经尝试过以下方法 root fx shadow highlight color CCFF99 感谢帮助 Look at 里海 css JavaFX 2
  • 窗格形状修改

    好吧 长话短说 我正在尝试创建一种聊天 消息系统 并且需要一点帮助 我正在尝试在容器上创建一个箭头 如下图所示 该图像是从 ControlsFX 及其 PopOver 窗口中取出的 我不能使用他们的弹出窗口小部件 因为它的行为与我使用它的目
  • JavaFX 2 中的组合框键值对

    我刚刚开始学习 JavaFX 2 现在我正在尝试构建一个示例应用程序 然后我就陷入了组合框 我在 JavaFX 中没有找到任何对组合框键值对的引用 组合框 javadoc 位于http docs oracle com javafx 2 ap
  • 在 64 位机器中创建 32 位 JavaFx Native Bundle

    我的机器和 IDE 的配置如下 产品版本 NetBeans IDE 7 2 内部版本 201207171143 爪哇 1 7 0 07 Java HotSpot TM 64 位服务器虚拟机 23 3 b01 系统 Windows 7 版本6
  • 企业 Web 应用程序中的 JavaFX - 经典网页的良好替代方案?

    我们为不同的客户开发了多种 Web 应用程序 从为那些可怜的管理员提供的简单 看起来很无聊的 CRUD 风格的 GUI 到为那些互联网用户提供的奇特的 AJAX 增强型 GUI 所有这些都基于 JSF 1 2 与 JBoss Seam 2
  • 在JavaFX中如何在表视图中添加带有数据的组合框

    我已经尝试了很多 但无法将数据库中的所有值填充到我的组合框表格单元格中 控制器 java public class controller GetConnection gc new GetConnection PreparedStatemen
  • JavaFX 2.0 FXML 子窗口

    经过多次搜索我发现了这个问题如何创建 javafx 2 0 应用程序 MDI https stackoverflow com questions 10915388 how to create a javafx 2 0 application
  • 如何在JavaFX中播放其他视频格式

    我正忙于电影 视频剪辑播放器 库 我想在 JavafX 中做到这一点 几乎 90 的视频剪辑都是 AVI 格式 由于多种原因 我无法隐藏电影 视频剪辑 我还希望该程序能够标记已播放的视频文件 从开始到结束 以便我知道观看了什么 因此程序需要
  • javafx tableview 中的快速过滤

    我需要在 javafx tableview 中实现一个具有大量数据 大约 100 000 的过滤器 我努力了本教程 http code makery ch blog javafx 2 tableview filter 它可以工作 但是与 s
  • 如何在 JavaFX 中设置滚动窗格的单位增量?

    The 滚动条 http docs oracle com javafx 2 api javafx scene control ScrollBar htmlJavaFX 中的类包含一个用于设置单位增量的属性 这就是我所追求的 但是我找不到如何
  • JavaFX 多线程 - 连接线程不会更新 UI

    我正在尝试创建一个加载程序对话框 用户可以在其中知道程序正在加载所请求的内容并且程序正在按预期运行 但正因为如此 我需要join 解析器线程和之前继续主线程 这使得对话框空白 解析器任务 java public class ParserTa
  • 解析节点的 CSS 属性

    也许我的 google fu 今天让我失望了 但我还没有找到一种方法来解析节点的任意属性以获得样式属性 例如 我希望能够询问节点 fx fill 的值是多少 如果有 一个简单的应用程序示例 我想为图表构建自定义图例 为此 我必须向渲染节点询
  • JavaFX 中画布的最大尺寸

    有谁知道JavaFX中画布的最大尺寸 从一些测试来看 它似乎是 8192 与 IE 相同 在我看来 这很奇怪 或许 可以修改吗 这可能取决于您的显卡的最大纹理大小 因为我相信 使用最常见的硬件加速渲染管道 画布最终将表示为纹理 我不太清楚
  • JavaFX 2.0 - 为 FXML 中的自定义组件创建操作处理程序

    我想在我的新组件中添加自定义操作 这个怎么做 示例代码 成分 public class MyCustomComponent extends Region public MyCustomComponent super this setOnMo

随机推荐

  • 如何修复预期响应包含数组但获得对象 ANGular js

    我是新人 在调用服务后使用资源模块很难得到这个错误 任何人都可以修改我在代码中出错的地方 或者只是修改其中需要纠正的部分 谢谢 即将到来的数据格式 brands Array 1 0 Object v 0 id 5251a4a34f232fc
  • 配置文件与 gcc/g++ 和 ld 的链接时间

    我正在使用 g 编译和链接一个由大约 15 个 c 源文件和 4 个共享对象文件组成的项目 最近 链接时间增加了一倍多 但我没有可用的 makefile 的历史记录 有没有办法分析 g 以查看链接的哪一部分花费了很长时间 Edit 当我注意
  • 如何确定 PHP 字符串是否仅包含纬度和经度

    我必须使用可能包含纬度 经度数据的字符串 如下所示 query 33 805789 151 002060 query 33 805789 151 002060 query OVER HERE 33 805789 151 002060 就我的
  • 保存片段状态操作栏选项卡

    我正在构建一个由带有选项卡的操作栏组成的应用程序 每个选项卡都包含一个片段 其中一个片段有需要加载的数据 所以我希望数据只在第一次加载 当用户更改选项卡并返回到包含该片段的选项卡时 不会等待再次加载数据 我假设我必须使用某种方式保存数据 以
  • Java中用外国字符替换英文字符的方法?

    在 PHP 中我会使用这个 text Je prends une th chaud s il vous pla t search array etc replace array e i e etc text str replace sear
  • 对于英语单词来说,什么是好的哈希函数?

    我有一长串英语单词 我想对它们进行哈希处理 什么是好的哈希函数 到目前为止 我的散列函数对字母的 ASCII 值求和 然后对表大小取模 我正在寻找有效且简单的东西 简单地对字母求和并不是一个好的策略 因为排列会给出相同的结果 这个 djb2
  • 如何在 MVC 中创建启动屏幕

    我想在单击保存按钮时创建一个启动屏幕 显示消息 您的数据正在保存 请稍候 我使用 MVC 5 进行开发 下面我创建了一个div div style margin 0px padding 0px width 100 height 100 ba
  • 我应该为我的个人项目使用软件托管解决方案吗?

    现在 我将所有项目都保存在笔记本电脑上 我认为我不应该这样做 而是使用版本控制系统并从外部托管存储库 Google Code SourceForge 等 将它们签入 签出 我在这里看到了几个好处 首先 如果我的计算机崩溃和烧毁或者我的外部硬
  • JavaScript 注释

    有 JavaScript 注释吗 当然 JavaScript 没有它们 但是是否有其他库或建议的语言扩展 例如 type folder otherjsmodule foo function foo folder otherjsmodule
  • 使用knockout js上传文件

    使用knockout js 无法上传文件 我尝试过下面的代码但不起作用 请指出我哪里做错了 这是我的文件控件和按钮 我无法将所选文件从客户端发送到服务器 请建议最好的方法是什么
  • 无法渲染动画

    我正在尝试学习如何使用 R 和 gganimate 获得漂亮的动画图形 但遇到了一些问题 当我尝试为图形设置动画时 R 似乎编辑了一些 PNG 文件 但随后无法渲染它们 这是我使用的代码示例 首先加载 tidyverse gganimate
  • 使用 jquery 切换显示\隐藏但具有多个 id

    我有一个 php 页面 它生成多个 UL 和每个 UL 上方的链接 如下所示 a title Category one Category one a ul li Subcategory li li li li Subcategory li
  • 获取异步任务的结果

    我想从异步任务中获取结果 如果我使用task execute get 我的UI将被冻结 我希望我的异步任务是独立的类 所以我不想将结果处理代码放在 onPostExecute 中 我在这里找到了一些有关异步任务回调数据的信息 http bl
  • 如何改进 Excel 数据连接的刷新?

    和许多人一样 我有一个电子表格 可以从 40 多个文本文件中提取数据作为数据源 文本文件来自另一个应用程序 需要定期更新到 Excel 中 数据源文件和电子表格集需要能够复制并在不同的系统上运行 这就是 Excel 惊人地无法支持从电子表格
  • com.opensymphony.xwork2.inject.DependencyException: com.opensymphony.xwork2.inject.ContainerImpl$MissingDependencyException

    在部署我的战争文件时 我收到以下异常 SEVERE Exception starting filter struts2 com opensymphony xwork2 inject ContainerImpl MissingDependen
  • Python中列表的大小调整因子是多少

    例如 ArrayListJava 中的 s 的大小调整因子为 2 ArrayList当空间不足时 该数组的所有元素都会转移到一个新数组 该新数组的大小是原始数组的 2 倍 由于 Python 列表 数组本质上是动态的 那么它们的大小调整因子
  • 将宽字符串文字与字符串宏相结合

    我有一个字符串宏 如下所示 define APPNAME MyApp 现在我想通过执行以下操作来使用此宏构造一个宽字符串 const wchar t AppProgID APPNAME L Document 但是 这会生成 连接不匹配的字符
  • Google Sheets 组合骰子的公式

    情况 我正在创建一个骰子符号 清理 公式 以便将类似的骰子组合起来 例如 1D6 1D6 将变为 2d6 使事情变得复杂的是 负掷骰 如 1D6 不能汇总到最终结果中 意思是 1D6 1D6 不等于 0 为什么 因为 1D6 并不等于每次滚
  • sqlite3:由于未最终声明而无法关闭

    我有两个 sqlite 连接并按如下方式执行 CMyDatabase 是 sqlite3 的派生类 CMyDatabase dbConnection1 new CMyDatabase dbConnection1 gt OpenDataBas
  • 布局更改时的动画

    每次调整窗口大小时 JavaFX 中的基本 FlowPane 都会对其中的项目进行布局 然而 没有动画 结果相当不和谐 我在 FlowPane 内每个节点的layoutX 和layoutY 属性上连接了一个更改侦听器 结果或多或少有效 但有