javafx - 警报和阶段焦点
javafx - Alert and stage focus
对于我的应用程序,我需要知道应用程序的 window 何时处于焦点状态。为此,我可以使用 primaryStage.focusedProperty().addListener(..)
,它会警告我舞台焦点的变化。
但我已经意识到,以 primaryStage
作为所有者打开一个 Alert
并且模态设置为 WINDOW_MODAL
会使 primaryStage
失去焦点(即使 window 实际上专注于现实,或者至少在 Windows 中)。
现在我遇到的问题是我想知道 Window
何时聚焦,而不仅仅是 primaryStage
;或者至少知道 Alert
是否被聚焦,但我找不到如何聚焦。
我曾尝试在警报上使用类似的属性(如 onShowing
和 onHiding
)但没有成功。
这是一段代码来说明我的问题:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.focusedProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("primaryStage focused : "+newValue);
});
primaryStage.show();
//create a basic alert
Alert alert = new Alert(Alert.AlertType.INFORMATION,"This is a test");
alert.initModality(Modality.WINDOW_MODAL); //will block input to its owner window
alert.initOwner(primaryStage);
alert.onShowingProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("alert onShowing : "+newValue);
});
alert.onShownProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("alert onShown : "+newValue);
});
alert.onHidingProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("alert onHiding : "+newValue);
});
alert.onHiddenProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("alert onHidden : "+newValue);
});
alert.showAndWait();
}
public static void main(String[] args) {
launch(args);
}
}
基本上它会打印这个:
primaryStage focused : true //stage is created and displayed
primaryStage focused : false //alert is displayed, stage loses focus. alt+tab changes nothing
primaryStage focused : true //alert closed by pressing 'ok'
这很奇怪,因为它应该生成所有其他打印件。
理想情况下我还需要一个 :
primaryStage focused : true //stage is created and displayed
primaryStage focused : false //alert is displayed, stage loses focus
alert focused : true //alert gains focus
alert focused : false //alt+tab to an other window
alert focused : true //alt+tab back to this window
alert focused : false //alert closed by pressing 'ok'
primaryStage focused : true //stage regains focus
或类似的东西。有没有人有实现此目标的想法,或者这 primaryStage
失去对 WINDOW_MODAL
Alert
问题的关注是我应该报告的问题吗?
所以我终于找到了解决方法。警报中使用的每个 buttonType
都可以确定它是否获得焦点。这个 属性 在 alt+tab 时也会改变,因此我们可以监控每个 buttonType
被使用,看看是否只有一个被聚焦。
这是我的解决方案,有点老套,但实现了我想要的:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.focusedProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("PrimaryStage focused : "+newValue);
});
primaryStage.show();
//create a basic alert
Alert alert = new Alert(Alert.AlertType.CONFIRMATION,"This is a test");
alert.initModality(Modality.WINDOW_MODAL); //will block input to its owner window
alert.initOwner(primaryStage);
alert.getButtonTypes().forEach(buttonType -> {
//add a focus listnener for each buttonType of this alert
alert.getDialogPane().lookupButton(buttonType).focusedProperty().addListener((observable, oldValue, newValue) -> {
System.out.println(buttonType.getText()+" focused : "+newValue);
System.out.println("Alert focused : "+isAlertFocused(alert));
});
});
alert.showAndWait();
}
/** Looks all {@link ButtonType} used in the given {@link Alert} and checks if any of them is
* focused, hence if the {@link Alert} is being focused
*
* @param alert the {@link Alert} we want to check the focused status from
* @return true if the alert is focused, false otherwise
*/
private boolean isAlertFocused(Alert alert){
if(alert == null){
return false;
}
final boolean[] focused = {false};
alert.getButtonTypes().forEach(buttonType -> {
focused[0] = focused[0] || alert.getDialogPane().lookupButton(buttonType).isFocused();
});
return focused[0];
}
public static void main(String[] args) {
launch(args);
}
}
我想我也可以通过在 isAlertFocused(Alert alert)
方法中添加 alert.getDialogPane().isFocused()
检查来将此方法扩展到对话框,但这超出了我的问题范围。
对于我的应用程序,我需要知道应用程序的 window 何时处于焦点状态。为此,我可以使用 primaryStage.focusedProperty().addListener(..)
,它会警告我舞台焦点的变化。
但我已经意识到,以 primaryStage
作为所有者打开一个 Alert
并且模态设置为 WINDOW_MODAL
会使 primaryStage
失去焦点(即使 window 实际上专注于现实,或者至少在 Windows 中)。
现在我遇到的问题是我想知道 Window
何时聚焦,而不仅仅是 primaryStage
;或者至少知道 Alert
是否被聚焦,但我找不到如何聚焦。
我曾尝试在警报上使用类似的属性(如 onShowing
和 onHiding
)但没有成功。
这是一段代码来说明我的问题:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.focusedProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("primaryStage focused : "+newValue);
});
primaryStage.show();
//create a basic alert
Alert alert = new Alert(Alert.AlertType.INFORMATION,"This is a test");
alert.initModality(Modality.WINDOW_MODAL); //will block input to its owner window
alert.initOwner(primaryStage);
alert.onShowingProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("alert onShowing : "+newValue);
});
alert.onShownProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("alert onShown : "+newValue);
});
alert.onHidingProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("alert onHiding : "+newValue);
});
alert.onHiddenProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("alert onHidden : "+newValue);
});
alert.showAndWait();
}
public static void main(String[] args) {
launch(args);
}
}
基本上它会打印这个:
primaryStage focused : true //stage is created and displayed
primaryStage focused : false //alert is displayed, stage loses focus. alt+tab changes nothing
primaryStage focused : true //alert closed by pressing 'ok'
这很奇怪,因为它应该生成所有其他打印件。 理想情况下我还需要一个 :
primaryStage focused : true //stage is created and displayed
primaryStage focused : false //alert is displayed, stage loses focus
alert focused : true //alert gains focus
alert focused : false //alt+tab to an other window
alert focused : true //alt+tab back to this window
alert focused : false //alert closed by pressing 'ok'
primaryStage focused : true //stage regains focus
或类似的东西。有没有人有实现此目标的想法,或者这 primaryStage
失去对 WINDOW_MODAL
Alert
问题的关注是我应该报告的问题吗?
所以我终于找到了解决方法。警报中使用的每个 buttonType
都可以确定它是否获得焦点。这个 属性 在 alt+tab 时也会改变,因此我们可以监控每个 buttonType
被使用,看看是否只有一个被聚焦。
这是我的解决方案,有点老套,但实现了我想要的:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.focusedProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("PrimaryStage focused : "+newValue);
});
primaryStage.show();
//create a basic alert
Alert alert = new Alert(Alert.AlertType.CONFIRMATION,"This is a test");
alert.initModality(Modality.WINDOW_MODAL); //will block input to its owner window
alert.initOwner(primaryStage);
alert.getButtonTypes().forEach(buttonType -> {
//add a focus listnener for each buttonType of this alert
alert.getDialogPane().lookupButton(buttonType).focusedProperty().addListener((observable, oldValue, newValue) -> {
System.out.println(buttonType.getText()+" focused : "+newValue);
System.out.println("Alert focused : "+isAlertFocused(alert));
});
});
alert.showAndWait();
}
/** Looks all {@link ButtonType} used in the given {@link Alert} and checks if any of them is
* focused, hence if the {@link Alert} is being focused
*
* @param alert the {@link Alert} we want to check the focused status from
* @return true if the alert is focused, false otherwise
*/
private boolean isAlertFocused(Alert alert){
if(alert == null){
return false;
}
final boolean[] focused = {false};
alert.getButtonTypes().forEach(buttonType -> {
focused[0] = focused[0] || alert.getDialogPane().lookupButton(buttonType).isFocused();
});
return focused[0];
}
public static void main(String[] args) {
launch(args);
}
}
我想我也可以通过在 isAlertFocused(Alert alert)
方法中添加 alert.getDialogPane().isFocused()
检查来将此方法扩展到对话框,但这超出了我的问题范围。