如何通过屏幕坐标获取JavaFX的Stage?跨Stage事件传递需求
Great question! In JavaFX, there isn't a direct built-in API to retrieve a Stage by screen coordinates, but we can work around this by iterating through all open windows and checking if the target coordinates fall within their bounds. Let's break down the solution and fix your current implementation issues.
Core Problem in Your Current Code
Your existing onMouseClicked method fires the event on the sender stage's own node, which triggers the same onMouseClicked handler recursively—this is why you're seeing the InvocationTargetException. We need to instead:
- Locate the correct target Stage/Window at your simulated cursor's coordinates
- Find the exact node under those coordinates in the target Stage
- Fire the event on that target node (not the sender's node)
Step-by-Step Solution
1. Add a Helper Method to Locate the Target Window
First, create a method to scan all open windows and find the one that contains your target screen coordinates. We'll exclude the sender window itself and any popups (like your simulated cursor) to avoid false matches:
import javafx.stage.Window; import javafx.geometry.Rectangle2D; private Window getTargetWindow(double screenX, double screenY, Window senderWindow) { for (Window window : Window.getWindows()) { // Skip invisible windows, the sender window, and popups if (!window.isShowing() || window.equals(senderWindow) || window instanceof javafx.stage.Popup) { continue; } // Check if coordinates are within the window's screen bounds Rectangle2D windowBounds = new Rectangle2D( window.getX(), window.getY(), window.getWidth(), window.getHeight() ); if (windowBounds.contains(screenX, screenY)) { return window; } } return null; }
2. Modify the onMouseClicked Method to Target the Correct Stage
Update your event handler to find the target window, locate the node under the coordinates, and fire the event correctly:
import javafx.scene.Point2D; import javafx.stage.Window; public void onMouseClicked(MouseEvent mouseEvent) { // Get the simulated cursor's screen coordinates double screenX = cursorPosX; double screenY = cursorPosY; // Get the sender's own window to exclude it from search Window senderWindow = ((Node) mouseEvent.getSource()).getScene().getWindow(); Window targetWindow = getTargetWindow(screenX, screenY, senderWindow); if (targetWindow == null) { System.out.println("No target stage found at (" + screenX + ", " + screenY + ")"); return; } // Convert screen coordinates to the target scene's local coordinates Scene targetScene = targetWindow.getScene(); Point2D scenePoint = targetScene.screenToLocal(screenX, screenY); double sceneX = scenePoint.getX(); double sceneY = scenePoint.getY(); // Find the exact node under the coordinates in the target scene Node targetNode = targetScene.getRoot().pick(sceneX, sceneY); // Fallback to the scene root if no specific node is found if (targetNode == null) { targetNode = targetScene.getRoot(); } // Convert scene coordinates to the target node's local coordinates Point2D localPoint = targetNode.sceneToLocal(sceneX, sceneY); double localX = localPoint.getX(); double localY = localPoint.getY(); // Create a properly configured MouseEvent for the target node MouseEvent clickEvent = new MouseEvent( MouseEvent.MOUSE_CLICKED, localX, localY, // Node-local coordinates sceneX, sceneY, // Scene coordinates MouseButton.PRIMARY, 1, // Click count true, true, true, true, true, true, true, true, true, true, null // No event source needed here ); // Fire the event on the target node targetNode.fireEvent(clickEvent); System.out.println("Successfully fired click event to: " + targetNode.getClass().getSimpleName()); }
3. Key Fixes & Explanations
- Avoid Recursion: We explicitly exclude the sender window from the search, so we never fire the event on the sender's own node again.
- Coordinate Conversion: JavaFX events rely on correct coordinate contexts (node-local, scene, screen). We convert the screen coordinates step-by-step to match the target node's local space.
- Node Picking: Using
scene.getRoot().pick()ensures we target the exact UI element under the cursor, not just the Stage itself.
Testing the Solution
When you run your modified code:
- Move the simulated cursor in the "Sender" stage over the "Receiver" stage
- Click in the "Sender" stage—you should see the "MOUSE CLICKED!" message from the Receiver's controller, with no recursive exceptions.
内容的提问来源于stack exchange,提问作者beid

