You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 15:59:56