这是我在这里的第一篇文章-请注意:)
我想做的是让控件/窗口:
- 属于JavaFX阶段
- 点击时不会从拥有的舞台上窃取焦点
- 可以拖动到所属阶段的边界之外
我已经能够通过两种方式实现这一点;然而,两者都有缺陷。首先,我在舞台的内容中进行了可拖动的控制;但是,它们不能被拖到舞台的区域之外。第二,我把它们创建为可拖动的弹出菜单,固定在舞台上;然而,即使其他窗口被移动到所属阶段的顶部,它们仍然位于所有之上。
我想问的是:有没有任何方法可以将控件拖到它拥有的Stage的边界之外;或者让弹出菜单不总是出现在所有内容的顶部
我在这里发现了一个类似的问题,解释了Popup在顶部的问题(Javafx Popup在失去焦点时不会隐藏在其他应用程序后面)。但是没有可接受的解决方案(我不希望弹出窗口在阶段或应用程序失去焦点时"隐藏")。
感谢阅读。如果你能提出什么建议,我们将不胜感激。
下面是我尝试使用MoveablePopup的MCV示例。它工作得很好,除非你把额外的窗户盖在上面。
package moveablepopuptest;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Popup;
import javafx.stage.Stage;
public class MoveablePopupTest extends Application
{
@Override
public void start(Stage primaryStage)
{
// Set up Primary Stage:
StackPane root = new StackPane();
Scene scene = new Scene(root, 400, 400);
primaryStage.setTitle("MoveablePopup Test");
primaryStage.setScene(scene);
primaryStage.show();
// Show a MoveablePopup for an associated node in the Stage:
MoveablePopup popup = new MoveablePopup();
popup.setTitle("Extension");
Pane popupContent = new Pane();
popupContent.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
popupContent.setMinSize(200, 200);
popup.setContent(popupContent);
popup.show(root, 0, 0);
// When Primary Stage is moved, re-anchor the popup:
ChangeListener<Number> windowMoveListener = (ObservableValue<? extends Number> observable, Number oldValue, Number newValue) ->
{
popup.anchorToOwner();
};
primaryStage.xProperty().addListener(windowMoveListener);
primaryStage.yProperty().addListener(windowMoveListener);
}
public static void main(String[] args)
{
launch(args);
}
public class MoveablePopup extends Popup
{
private final BorderPane bgPane = new BorderPane();
private final DoubleProperty ownerXAnchorProperty = new SimpleDoubleProperty(0);
private final DoubleProperty ownerYAnchorProperty = new SimpleDoubleProperty(0);
private final Label titleLabel = new Label("Title");
private Point2D lastMouse = null;
private Node content = null;
public MoveablePopup()
{
// Don't want Esc to close the Popup:
setHideOnEscape(false);
// Create a border:
bgPane.setBorder(new Border(new BorderStroke(Color.DARKORANGE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(2))));
// Create a title bar for the top, which will also function as a handle to move the Popup:
StackPane barPane = new StackPane();
titleLabel.setTextFill(Color.WHITE);
barPane.getChildren().add(titleLabel);
barPane.setCursor(Cursor.MOVE);
barPane.setBackground(new Background(new BackgroundFill(Color.DARKORANGE, CornerRadii.EMPTY, Insets.EMPTY)));
barPane.setMaxSize(Double.MAX_VALUE, 24);
bgPane.setTop(barPane);
// Add drag/anchoring functionality to the Popup
barPane.setOnMousePressed((MouseEvent event) ->
{
lastMouse = new Point2D(event.getScreenX(), event.getScreenY());
});
barPane.setOnMouseDragged((MouseEvent event) ->
{
double moveX = event.getScreenX() - lastMouse.getX();
double moveY = event.getScreenY() - lastMouse.getY();
ownerXAnchorProperty.set(ownerXAnchorProperty.doubleValue()+moveX);
ownerYAnchorProperty.set(ownerYAnchorProperty.doubleValue()+moveY);
lastMouse = new Point2D(event.getScreenX(), event.getScreenY());
anchorToOwner();
});
getContent().add(bgPane);
}
@Override
protected void show()
{
super.show();
anchorToOwner();
}
public void setContent(Node content)
{
this.content = content;
bgPane.setCenter(content);
}
public void setTitle(String title)
{
titleLabel.setText(title);
}
// Repositions the MoveablePopup so that it's relationship to
// the owning Node's location is maintained:
public final void anchorToOwner()
{
if (getOwnerNode() != null)
{
Point2D screenPoint = getOwnerNode().localToScreen(0, 0);
setX(screenPoint.getX() + ownerXAnchorProperty.doubleValue());
setY(screenPoint.getY() + ownerYAnchorProperty.doubleValue());
}
}
}
}
创建一个新阶段,该阶段将成为"弹出窗口":
Stage childStage = new Stage();
childStage.initModality(Modality.WINDOW_MODAL);
childStage.setTitle("Title of popup");
childStage.setScene(YOUR_SCENE);
childStage.sizeToScene();
childStage.setResizable(false);
childStage.show();
正如您所发现的,舞台内发生的一切都停留在舞台内(请原谅双关语)。因此,如果您需要在舞台外显示一些内容,我们可以排除除Stage
本身之外的所有内容。您可以创建一个新阶段,并将您的初始阶段分配给所有者。Stage
作为本机窗口的表示,将在单击时抢占焦点。但是,可以在初始阶段请求重新聚焦。这应该勾选你的要求框。
我不确定您试图实现的用例,但JFXtra为现有的JavaFX API提供了一些扩展,包括通知和MDI窗口。看看这对你有没有帮助。