JavaFX周期表鼠标事件失效问题及多节点事件检测咨询
解决JavaFX周期表面板MouseExited事件丢失问题
我开发了一款周期表应用,实现了鼠标悬停元素Pane时放大并显示更多信息的功能,但鼠标快速移动或离开边缘面板时,MouseExited事件无法被检测到。相关代码如下:
缩放动画逻辑
public void onScale(int i){ double currentScaleX = Panes[i].getScaleX(); double currentScaleY = Panes[i].getScaleY();//存储当前缩放比例 if (currentScaleX == 1.0 && currentScaleY == 1.0) {//验证缩放比例为原始值(等于1) TranslateTransition translate = new TranslateTransition(Duration.millis(50), Symbol[i]); translate.setToY(-4); translate.playFromStart();//将元素符号上移一点 TranslateTransition tratn = new TranslateTransition(Duration.millis(50), AtomicNumber[i]); tratn.setToY(-2); tratn.playFromStart(); TranslateTransition tratm = new TranslateTransition(Duration.millis(50), AtomicMass[i]); tratm.setToY(-2); tratm.playFromStart(); TranslateTransition trnm = new TranslateTransition(Duration.millis(50), Name[i]); trnm.setToY(3); trnm.playFromStart(); AtomicMass[i].setVisible(true); OxidationNumber[i].setVisible(true);//显示原子量和氧化数 ScaleTransition scale = new ScaleTransition(Duration.millis(100), Panes[i]); scale.setToX(1.5); scale.setToY(1.5); scale.playFromStart(); //将面板置于前景(注意:与.tofront方法不同,这不会影响其在子列表中的位置) Panes[i].setViewOrder(-1.0); Panes[i].getParent().setViewOrder(-1.0); } } public void offScale(int i){ double currentScaleX = Panes[i].getScaleX(); double currentScaleY = Panes[i].getScaleY(); if (currentScaleX > 1.0 && currentScaleY > 1.0) { TranslateTransition translate = new TranslateTransition(Duration.millis(50), Symbol[i]); translate.setToY(0); translate.playFromStart(); TranslateTransition tratn = new TranslateTransition(Duration.millis(50), AtomicNumber[i]); tratn.setToY(0); tratn.playFromStart(); TranslateTransition tratm = new TranslateTransition(Duration.millis(50), AtomicMass[i]); tratm.setToY(0); tratm.playFromStart(); TranslateTransition trnm = new TranslateTransition(Duration.millis(50), Name[i]); trnm.setToY(0); trnm.playFromStart(); AtomicMass[i].setVisible(false); OxidationNumber[i].setVisible(false); ScaleTransition scale = new ScaleTransition(Duration.millis(100), Panes[i]); scale.setToX(1.0); scale.setToY(1.0); scale.playFromStart(); Panes[i].setViewOrder(0.0); Panes[i].getParent().setViewOrder(0.0); } }
事件调用代码
@FXML private void MouseEntered(MouseEvent event) { Pane pane = (Pane) event.getSource();//基于触发事件的Pane创建局部变量 onScale(Integer.parseInt(pane.getId())); for(int i=1; i<=118; i++){ offScale(i); } } @FXML private void MouseExited(MouseEvent event) { Pane pane = (Pane) event.getSource(); offScale(Integer.parseInt(pane.getId())); }
事件绑定代码
Panes[i].setOnMouseEntered(this::MouseEntered); Panes[i].setOnMouseExited(this::MouseExited);
解决方案建议
1. 优化事件触发逻辑,避免批量操作冲突
当前MouseEntered中遍历所有面板执行offScale,会干扰动画执行、浪费性能,甚至覆盖正常事件响应。维护一个当前激活面板的索引,仅对之前激活的面板执行关闭操作:
private int activePaneIndex = -1; // 记录当前激活的面板索引 @FXML private void MouseEntered(MouseEvent event) { Pane pane = (Pane) event.getSource(); int currentIndex = Integer.parseInt(pane.getId()); // 仅关闭之前激活的面板 if (activePaneIndex != -1 && activePaneIndex != currentIndex) { offScale(activePaneIndex); } onScale(currentIndex); activePaneIndex = currentIndex; } @FXML private void MouseExited(MouseEvent event) { Pane pane = (Pane) event.getSource(); int currentIndex = Integer.parseInt(pane.getId()); // 确保关闭的是当前激活的面板 if (activePaneIndex == currentIndex) { offScale(currentIndex); activePaneIndex = -1; } }
2. 启用边界拾取,确保事件检测准确
默认情况下,JavaFX节点仅在鼠标悬停于非透明区域时触发事件。给所有面板设置setPickOnBounds(true),只要鼠标在面板边界范围内就触发事件,避免边缘区域的事件丢失:
// 在初始化循环中添加 Panes[i].setPickOnBounds(true);
3. 添加动画状态锁,防止重复触发
同一面板的动画未完成时,禁止重复触发动画,避免状态混乱:
private boolean[] isAnimating = new boolean[119]; // 对应1-118号元素 public void onScale(int i){ if (isAnimating[i]) return; double currentScaleX = Panes[i].getScaleX(); double currentScaleY = Panes[i].getScaleY(); if (currentScaleX == 1.0 && currentScaleY == 1.0) { isAnimating[i] = true; // ... 原有动画代码 ... // 最后一个动画结束后重置状态 scale.setOnFinished(e -> isAnimating[i] = false); } } public void offScale(int i){ if (isAnimating[i]) return; double currentScaleX = Panes[i].getScaleX(); double currentScaleY = Panes[i].getScaleY(); if (currentScaleX > 1.0 && currentScaleY > 1.0) { isAnimating[i] = true; // ... 原有动画代码 ... scale.setOnFinished(e -> isAnimating[i] = false); } }
4. 全局鼠标追踪替代方案
如果上述方法仍有问题,可在父容器上绑定MouseMoved事件,实时检测鼠标位置,主动触发缩放逻辑:
// 假设父容器为parentPane parentPane.setOnMouseMoved(event -> { int hoveredIndex = -1; // 遍历所有面板,检测鼠标是否在面板范围内 for (int i = 1; i <= 118; i++) { Bounds bounds = Panes[i].localToScene(Panes[i].getBoundsInLocal()); if (bounds.contains(event.getSceneX(), event.getSceneY())) { hoveredIndex = i; break; } } // 更新激活状态 if (hoveredIndex != activePaneIndex) { if (activePaneIndex != -1) { offScale(activePaneIndex); } if (hoveredIndex != -1) { onScale(hoveredIndex); } activePaneIndex = hoveredIndex; } }); // 处理鼠标完全离开周期表区域的情况 parentPane.setOnMouseExited(event -> { if (activePaneIndex != -1) { offScale(activePaneIndex); activePaneIndex = -1; } });
内容的提问来源于stack exchange,提问作者Crynda
相关产品推荐
相关产品推荐

