如何在 JavaFX 中获取鼠标的位置?

Posted

技术标签:

【中文标题】如何在 JavaFX 中获取鼠标的位置?【英文标题】:How to get location of mouse in JavaFX? 【发布时间】:2013-05-14 04:25:29 【问题描述】:

我是 java(fx) 的初学者。 如何在 JavaFX 中获取 x 和 y 中的鼠标位置?我尝试使用 AWT 的 MouseInfo(也导入了它),但它不起作用。我还在 Ensembles 中看到了它的代码(在“高级阶段”中拖动球窗,这就是我需要做的,拖动我未装饰的 JavaFX 阶段),但它也不起作用。我将 FXML 与控制器一起使用,我想这是主要问题。我应该切换回单文件的简单 JavaFX 吗?我知道 FXML 更适合布局 UI,但我无法让很多这样的代码工作。还是我的控制器需要其他类型的代码?请尽可能使用 cmets 提供正确的代码。 如果您需要我的一些代码来检查,请随时询问。

【问题讨论】:

【参考方案1】:

您的问题中有几项 - 我将一次解决一项。

如何在 JavaFX 中获取鼠标在 x 和 y 中的位置?

将鼠标事件处理程序添加到要在其中跟踪鼠标位置的适当 JavaFX 组件。JavaFX 鼠标事件将报告多种不同类型的坐标。 x 和 y 坐标相对于被监控位置的节点的左上角。 sceneX 和 sceneY 坐标相对于场景左上角的 0,0 坐标。 screenX 和 screenY 坐标相对于当前屏幕的左上角 0,0 坐标。

这些坐标记录在MouseEvent 文档中。 Node 和 Scene 文档中提供了有关理解坐标系的额外信息。

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.*;

public class MouseLocationReporter extends Application 
  private static final String OUTSIDE_TEXT = "Outside Label";

  public static void main(String[] args)  launch(args); 

  @Override public void start(final Stage stage) 
    final Label reporter = new Label(OUTSIDE_TEXT);
    Label monitored = createMonitoredLabel(reporter);

    VBox layout = new VBox(10);
    layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10px;");
    layout.getChildren().setAll(
      monitored,
      reporter
    );
    layout.setPrefWidth(500);

    stage.setScene(
      new Scene(layout)
    );

    stage.show();
  

  private Label createMonitoredLabel(final Label reporter) 
    final Label monitored = new Label("Mouse Location Monitor");

    monitored.setStyle("-fx-background-color: forestgreen; -fx-text-fill: white; -fx-font-size: 20px;");

    monitored.setOnMouseMoved(new EventHandler<MouseEvent>() 
      @Override public void handle(MouseEvent event) 
        String msg =
          "(x: "       + event.getX()      + ", y: "       + event.getY()       + ") -- " +
          "(sceneX: "  + event.getSceneX() + ", sceneY: "  + event.getSceneY()  + ") -- " +
          "(screenX: " + event.getScreenX()+ ", screenY: " + event.getScreenY() + ")";

        reporter.setText(msg);
      
    );

    monitored.setOnMouseExited(new EventHandler<MouseEvent>() 
      @Override public void handle(MouseEvent event) 
        reporter.setText(OUTSIDE_TEXT);
      
    );

    return monitored;
  

我尝试使用 AWT 的 MouseInfo(也导入了它),但它不起作用。

不要这样做。混合不同的图形工具包(例如 Swing/AWT 和 JavaFX)是一个高级主题。通常,如果您正在编写 JavaFX 应用程序,请避免从 java.awt 命名空间和 javax.swing 命名空间导入任何内容。如果您有一个大型的、现有的基于 Swing 的应用程序或框架,您需要与您的 JavaFX 应用程序进行互操作,那么您才真正需要使用它们。在这种情况下,您就没有这种情况。

我还在 Ensembles 中看到了它的代码(在“高级阶段”中拖动球窗,这就是我需要做的,拖动我未装饰的 JavaFX 阶段),但它也不起作用。

我尝试了 Ensemble Advanced Stage 样本并拖动该阶段对我有用。

另一个用于在 JavaFX 中拖动未装饰舞台的示例是对 How to draw a clock with JavaFX 2? 的回答,其中包含 associated sample code。使时钟样本的未装饰舞台可拖动的方法是:

/** makes a stage draggable using a given node */
public static void makeDraggable(final Stage stage, final Node byNode) 
  final Delta dragDelta = new Delta();
  byNode.setOnMousePressed(new EventHandler<MouseEvent>() 
    @Override public void handle(MouseEvent mouseEvent) 
      // record a delta distance for the drag and drop operation.
      dragDelta.x = stage.getX() - mouseEvent.getScreenX();
      dragDelta.y = stage.getY() - mouseEvent.getScreenY();
      byNode.setCursor(Cursor.MOVE);
    
  );
  byNode.setOnMouseReleased(new EventHandler<MouseEvent>() 
    @Override public void handle(MouseEvent mouseEvent) 
      byNode.setCursor(Cursor.HAND);
    
  );
  byNode.setOnMouseDragged(new EventHandler<MouseEvent>() 
    @Override public void handle(MouseEvent mouseEvent) 
      stage.setX(mouseEvent.getScreenX() + dragDelta.x);
      stage.setY(mouseEvent.getScreenY() + dragDelta.y);
    
  );
  byNode.setOnMouseEntered(new EventHandler<MouseEvent>() 
    @Override public void handle(MouseEvent mouseEvent) 
      if (!mouseEvent.isPrimaryButtonDown()) 
        byNode.setCursor(Cursor.HAND);
      
    
  );
  byNode.setOnMouseExited(new EventHandler<MouseEvent>() 
    @Override public void handle(MouseEvent mouseEvent) 
      if (!mouseEvent.isPrimaryButtonDown()) 
        byNode.setCursor(Cursor.DEFAULT);
      
    
  );

我将 FXML 与控制器一起使用,我想这是主要问题。我应该切换回单文件的简单 JavaFX 吗?我知道 FXML 更适合布局 UI,但我无法让很多这样的代码工作。

对底层 JavaFX API 缺乏理解和熟悉可能是您的主要问题,而不是使用 FXML。但是,fxml 所暗示的额外复杂性以及网络上较轻的文档和示例可能会导致您的困难。如果使用 FXML 让您难以理解如何让一些 JavaFX 函数工作,我建议现在停止使用 FXML。使用 Java API 手动编写逻辑代码,遇到困难时参考Oracle JavaFX tutorials 和Ensemble sample code。

一旦您习惯了直接使用 JavaFX API 进行编码,就可以切换回使用 FXML 来处理包含许多 GUI 元素的大型项目。 FXML 元素和属性本身几乎完全基于标准 JavaFX API 的反映而构建。因此,如果您了解核心 JavaFX API,那么您也几乎了解 FXML 的所有内容。

请不要对这个答案发布后续 cmets(因为这个答案已经足够长了)。如果您有新问题,请创建一个新问题(每个问题一个问题)。

【讨论】:

Jewelsea 爵士你真棒(:总是加倍努力。谢谢 @jewelsea 我可以使用这个技巧来像 Windows 8 一样显示菜单吗? (表示鼠标移到角落时显示菜单) @UnKnown 不,据我所知,你不能。鼠标事件仅传递给场景。如果您有一个全屏应用程序,那么您可以这样做,但如果您的应用程序是窗口化或最小化的,并且您希望仅根据鼠标移动到屏幕一角来显示菜单,您就不能这样做。我不确定你会怎么做。您可能希望为此创建一个新问题。 这仅给出带有“.0”的整数值,那么我们如何才能找到双精度的绝对位置,例如:22.75 等。 我认为鼠标跟踪只适用于整数,至少就报告给 JavaFX 的情况而言,也许在操作系统窗口系统级别也是如此。【参考方案2】:

为了这个目的使用机器人怎么样?

http://docs.oracle.com/javase/1.5.0/docs/api/java/awt/Robot.html

使用机器人,它不同于将事件发布到 AWT 事件队列。事件在本机事件队列中生成。实际上,使用Robot.mouseMove,您不仅可以设置鼠标位置,也可以获取位置。

要获取鼠标位置,你可以坚持MouseInfo

import java.awt.MouseInfo;
// get the mouse's position
Point p = MouseInfo.getPointerInfo().getLocation();

它不起作用:您使用的是 Mac 吗?您的 JavaFX 版本是哪个?似乎是针对 FX8 更正的问题。仅适用于 mac,您可以使用

com.sun.glass.ui.Robot robot =
       com.sun.glass.ui.Application.GetApplication().createRobot();

// getPosition of the mouse in Mac
int x = robot.getMouseX(); 
int y = robot.getMouseY();

【讨论】:

我使用的是 Windows 8 x64、JavaFX 2 (jdk v7u21)。我昨天正在下载jdk 8,但是我的愚蠢的网络然后死了。 我可以在 Windows 上使用那个机器人(它只是用于测试自动化还是我可以在现实世界中使用它?)?尽管带有点的 MouseInfo 有效,但它同时提供了 x 和 y。(java.awt.Point[x=449,y=323]) 我需要将它们分开。该怎么做? 机器人也可以在 Windows 上工作:com.sun.glass.ui.Robot robot = com.sun.glass.ui.Application.GetApplication().createRobot(); int x = 机器人.getMouseX(); System.out.println("鼠标在x上的位置是:" + x); JavaFX 11 添加javafx.scene.robot.Robot【参考方案3】:

不幸的是,JavaFx 8 WindowEvent 不提供鼠标的 (x,y) 位置。我通过使用这样的 AWT MouseInfo 解决了这个问题(并且效果很好):

Tooltip t = new Tooltip();
Tooltip.install(yournode, t);
t.setOnShowing(ev -> // called just prior to being shown
                Point mouse = java.awt.MouseInfo.getPointerInfo().getLocation();
                Point2D local = yournode.screenToLocal(mouse.x, mouse.y);

                // my app-specific code to get the chart's yaxis value
                // then set the text as I want
                double pitch = yaxis.getValueForDisplay(local.getY()).doubleValue();
                double freq = AudioUtil.pitch2frequency(pitch);
                t.setText(String.format("Pitch %.1f:  %.1f Hz   %.1f samples", pitch, freq, audio.rate / freq));
        );

【讨论】:

以上是关于如何在 JavaFX 中获取鼠标的位置?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaFX 中的 SplitPane Divider 上检测鼠标拖动事件

JavaFX的窗体为“StageStyle.UNDECORATED”时,如何使用鼠标移动窗体

如何检查鼠标光标是不是在 Javafx 中的按钮上?

如何在javafx中用鼠标绘制选定的网格窗格范围

如何在javascript中获取鼠标相对于窗口视口的位置?

如何知道用户在 JavaFX TextArea 中选择了哪个文本字符串