JavaFX node.getChildren().remove() not working



我有一个带有4个按钮的JavaFX场景,用于让用户在4个不同的向导(应用程序是一个游戏)之间进行选择。在控制器中,我初始化可用的Wizards属性,当另一个玩家做出选择时,调用setAvailableWizards方法:那时我想从场景中删除与不再可用的wizard对应的按钮:

向导枚举:

public enum Wizard {
KING, PIXIE, SORCERER, WIZARD;
}

JavaFX控制器:

public class WizardController extends ViewObservable implements Initializable {
public HBox wizardsHBox;
private List<Wizard> availableWizards;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
availableWizards = Arrays.stream(values()).toList();
}
public void setAvailableWizards(List<Wizard> availableWizardsUpdate) {
List<Wizard> removed = new ArrayList<>(availableWizards);
removed.removeAll(availableWizardsUpdate);
availableWizards = availableWizardsUpdate;
System.out.println(wizardsHBox.getChildren());
removed.forEach(r -> {
Button toRemove = (Button) Gui.getStage().getScene().lookup("#" + r.toString().toLowerCase() + "Button");
wizardsHBox.getChildren().remove(toRemove);
System.out.println(wizardsHBox.getChildren());
});
}
public void handleKingButton(ActionEvent actionEvent) {
String chosenId = ((Button) actionEvent.getSource()).getId();
String chosenWizard = chosenId.substring(0, chosenId.indexOf("Button")).toUpperCase();
// notify to the server
}
}

FXML

<HBox fx:id="wizardsHBox" alignment="CENTER" layoutX="10.0" layoutY="144.0" prefHeight="332.0" prefWidth="599.0" spacing="20.0">
<children>
<Button fx:id="kingButton" mnemonicParsing="false" onAction="#handleKingButton" styleClass="wizard-btn">
<graphic>
<VBox alignment="CENTER" prefHeight="322.0" prefWidth="143.0" spacing="20.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="King" />
<ImageView fitHeight="193.0" fitWidth="117.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../images/wizards/king.png" />
</image>
</ImageView>
</children>
</VBox>
</graphic>
</Button>
<Button fx:id="sorcererButton" layoutX="229.0" layoutY="10.0" mnemonicParsing="false" onAction="#handleKingButton" styleClass="wizard-btn">
<graphic>
<VBox alignment="CENTER" prefHeight="322.0" prefWidth="143.0" spacing="20.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Sorcerer" />
<ImageView fitHeight="193.0" fitWidth="117.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../images/wizards/sorcerer.png" />
</image>
</ImageView>
</children>
</VBox>
</graphic>
</Button>
<Button fx:id="pixieButton" layoutX="320.0" layoutY="10.0" mnemonicParsing="false" onAction="#handleKingButton" styleClass="wizard-btn">
<graphic>
<VBox alignment="CENTER" prefHeight="322.0" prefWidth="143.0" spacing="20.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Pixie" />
<ImageView fitHeight="193.0" fitWidth="117.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../images/wizards/pixie.png" />
</image>
</ImageView>
</children>
</VBox>
</graphic>
</Button>
<Button fx:id="wizardButton" layoutX="410.0" layoutY="10.0" mnemonicParsing="false" onAction="#handleKingButton" styleClass="wizard-btn">
<graphic>
<VBox alignment="CENTER" prefHeight="322.0" prefWidth="143.0" spacing="20.0">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Wizard" />
<ImageView fitHeight="193.0" fitWidth="117.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="@../images/wizards/wizard.png" />
</image>
</ImageView>
</children>
</VBox>
</graphic>
</Button>
</children>
<padding>
<Insets left="20.0" right="20.0" />
</padding>
</HBox>

给定您的FXML文件,我假设您在应用程序启动时已经创建了这4个向导按钮。从表面上看,它们都有各自的fx:id

在你的课开始时添加它们,就像你添加wizardsHBox一样,然后为每个按钮创建一个setOnMouseClicked()事件处理程序来调用wizardsHBox.getChildren().remove(clickedButton)。当然,只是这样,不会跟踪所有可用的法师留下。因此,我们需要改变setAvailableWizards()方法的工作方式。

结果是这样的:

public class WizardController extends ViewObservable implements Initializable {
// Make sure they're 'private'.
@FXML
private Button kingButton, sorcererButton, pixieButton, wizardButton;
@FXML
private HBox wizardsHBox;
private List<Wizard> availableWizards;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
availableWizards = Arrays.stream(values()).toList();
kingButton.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// wizardsHBox.getChildren().remove(kingButton);
setAvailableWizards(kingButton, Wizard.KING);
}
});
sorcererButton.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// wizardsHBox.getChildren().remove(sorcererButton);
setAvailableWizards(sorcererButton, Wizard.SORCERER);
}
});
pixieButton.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// wizardsHBox.getChildren().remove(pixieButton);
setAvailableWizards(pixieButton, Wizard.PIXIE);
}
});

wizardButton.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
// wizardsHBox.getChildren().remove(wizardButton);
setAvailableWizards(wizardButton, Wizard.WIZARD);
}
});
}
public void setAvailableWizards(Button selectedWizard, Wizard wizardType) {
// Go over the 'availableWizards' list and check if any of the items match the provided 'wizardType' argument.
// If any of them does, remove it from the 'availableWizards' list and remove the button from the UI
for (Wizard wizard : availableWizards) {
if (wizard == wizardType) {
wizardsHBox.getChildren().remove(selectedWizard);
availableWizards.remove(wizardType);
wizardNotification(selectedWizard);
}
}
System.out.println(wizardsHBox.getChildren());
}
// I'm not sure what this is, but I'll assume it sends to the server which wizard was selected whenever the corresponding button is clicked.
// So I'll changed it up a bit. 
// (Renamed from 'handleKingButton' to 'wizardNotification')
// Also, from the original name, I'm assuming you have 'handlePixieButton', 'handleWizardButton', etc. - now you don't need them. This one is being reused regardless of which button you click.
public void wizardNotification(Button selectedWizard) {
// chosenId = button fx:id
String chosenId = selectedWizard.getId();
String chosenWizard = chosenId.substring(0, chosenId.indexOf("Button")).toUpperCase();
// notify to the server
}
}

注:这里可能有一些错别字,我直接在这里写了代码。

P.S.2。请确保您使用Task作为服务器通知,否则如果响应时间过长,UI可能会冻结。

最新更新