有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

javajavafx我无法更改TextField的文本,除非我从这个方法中更改它,而且它实际上只发生在这个方法中,在我的控制器类中

我的程序非常简单,只有两个场景,第二个场景只是说“这是第二个场景”,在第一个场景中,你必须选择一个文件夹,然后它会出现在一个不可编辑的文本字段中。您可以使用“后退”和“下一步”按钮在这两个场景之间来回切换。问题是,当我转到第二个场景,然后再回到第一个场景时,文本字段是空的,当它真的应该显示我之前选择的文件夹的路径时,但当我重新选择它再次显示的文件夹时,但每次我更改场景时,它都会消失。TextField的FX:id是TextField。我试着让它再次将文本设置为文件夹的路径,但这次是用“后退”方法设置后退按钮。但是,如果出于某种原因,它不在OpenFolder方法中,那么它永远不会起作用,因为它在其他任何地方都不起作用。我想知道为什么它只适用于OpenFolder方法,而不适用于其他任何地方。这是我的控制器类:

package sample;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;

import java.io.File;
import java.io.IOException;
import javafx.event.ActionEvent;

public class Controller {

private Stage stage;
private Scene scene;
private Parent root;

public Controller() {

}
@FXML
public void Next(ActionEvent event) throws IOException {

    Parent root = FXMLLoader.load(getClass().getResource("scene2.fxml"));
    stage = ((Stage)((Node)event.getSource()).getScene().getWindow()); 
    scene = new Scene(root);
    stage.setScene(scene);
    stage.show();
}
@FXML
public void Back(ActionEvent event) throws IOException{
    Parent root = FXMLLoader.load(getClass().getResource("scene1.fxml"));
    stage = ((Stage)((Node)event.getSource()).getScene().getWindow()); 
    scene = new Scene(root);
    stage.setScene(scene);
    stage.show();
}

@FXML
private TextField textfield = new TextField();
@FXML
private AnchorPane anchorid1;
@FXML
public static File thefolder;
@FXML
DirectoryChooser SongsOpener;
@FXML
public File OpenFolder(){
    final DirectoryChooser SongsOpener = new DirectoryChooser();
    stage = (Stage) anchorid1.getScene().getWindow();
    thefolder = SongsOpener.showDialog(stage);
    if (thefolder != null){
        textfield.setText(thefolder.getAbsolutePath());
    }
    return thefolder;
}
}

这是我的FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>

<AnchorPane fx:id="anchorid1" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="411.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <children>
      <Text layoutX="326.0" layoutY="76.0" strokeType="OUTSIDE" strokeWidth="0.0" text="This is scene 1">
         <font>
            <Font size="33.0" />
         </font>
      </Text>
      <Button layoutX="939.0" layoutY="373.0" mnemonicParsing="false" onAction="#Next" text="Next" />
      <Label layoutX="63.0" layoutY="117.0" prefHeight="18.0" prefWidth="122.0" text="Songs folder :">
         <font>
            <Font size="20.0" />
         </font></Label>
      <Button layoutX="888.0" layoutY="121.0" mnemonicParsing="false" onAction="#OpenFolder">
         <graphic>
            <ImageView fitHeight="18.0" fitWidth="17.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@../toppng.com-folder-icon-png-transparent-black-and-white-folder-ico-1589x1366.png" />
               </image>
            </ImageView>
         </graphic>
      </Button>
      <TextField fx:id="textfield" layoutX="190.0" layoutY="119.0" prefHeight="23.0" prefWidth="685.0" />
   </children>
</AnchorPane>

这里显示了我选择image文件夹后文本字段中的路径

但是,当我转到下一个场景并返回时,它消失了:image


共 (1) 个答案

  1. # 1 楼答案

    每次加载FXML文件时,FXMLLoader都会创建一组与FXML文件中的元素相对应的新控件。因此,如果重新加载第一个视图,就会得到一个新的TextField,它显然不包含与前一个TextField相同的文本,除非显式设置文本

    因此,您可以避免每次重新加载FXML文件(下面的第一个解决方案),或者创建一种机制,通过该机制,您可以在每次加载FXML时使用正确的值更新文本字段(下面的第二个解决方案,使用模型和绑定更新文本字段)

    还请注意,每次更改视图时,绝对没有理由创建新的Scene。只需使用一个场景并替换它的root

    解决方案加载每个视图一次

    解决这个问题的一种方法是只加载每个FXML一次,然后在控制器中的两个视图之间切换:

    App.java

    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    
    
    public class App extends Application {
    
    
        @Override
        public void start(Stage stage) throws IOException {
            FXMLLoader loader1 = new FXMLLoader(getClass().getResource("View1.fxml"));
            Parent view1 = loader1.load();
            
            FXMLLoader loader2 = new FXMLLoader(getClass().getResource("View2.fxml"));
            Parent view2 = loader2.load();
            
            Scene scene = new Scene(view1);
            
            View1Controller controller1 = loader1.getController();
            View2Controller controller2 = loader2.getController();
            
            controller1.setOnNext(() -> scene.setRoot(view2));
            controller2.setOnBack(() -> scene.setRoot(view1));
            
            stage.setScene(scene);
            stage.show();
        }
    
    
        public static void main(String[] args) {
            launch();
        }
    
    }
    

    View1Controller.java

    import java.io.File;
    
    import javafx.fxml.FXML;
    import javafx.scene.control.TextField;
    import javafx.stage.DirectoryChooser;
    import javafx.stage.Stage;
    
    public class View1Controller {
    
        private Runnable onNext = () -> {};
        
        @FXML
        private TextField textfield ;
        
        public void setOnNext(Runnable onNext) {
            this.onNext = onNext ;
        }
        
        @FXML
        private void next() {
            onNext.run();
        }
        
        @FXML
        public void openFolder(){
            final DirectoryChooser songsOpener = new DirectoryChooser();
            Stage stage = (Stage) textfield.getScene().getWindow();
            File thefolder = songsOpener.showDialog(stage);
            if (thefolder != null){
                textfield.setText(thefolder.getAbsolutePath());
            }
        }
    }
    

    View2Controller.java

    import javafx.fxml.FXML;
    
    public class View2Controller {
        
        private Runnable onBack = () -> {} ;
        
        public void setOnBack(Runnable onBack) {
            this.onBack = onBack ;
        }
        
        @FXML
        private void back() {
            onBack.run();
        }
    
    }
    

    View1.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.control.TextField?>
    <?import javafx.scene.image.Image?>
    <?import javafx.scene.image.ImageView?>
    <?import javafx.scene.layout.AnchorPane?>
    <?import javafx.scene.text.Font?>
    <?import javafx.scene.text.Text?>
    
    <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="411.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jamesd.examples.switchviews.View1Controller">
       <children>
          <Text layoutX="326.0" layoutY="76.0" strokeType="OUTSIDE" strokeWidth="0.0" text="This is scene 1">
             <font>
                <Font size="33.0" />
             </font>
          </Text>
          <Button layoutX="939.0" layoutY="373.0" mnemonicParsing="false" onAction="#next" text="Next" />
          <Label layoutX="63.0" layoutY="117.0" prefHeight="18.0" prefWidth="122.0" text="Songs folder :">
             <font>
                <Font size="20.0" />
             </font></Label>
          <Button layoutX="888.0" layoutY="121.0" mnemonicParsing="false" onAction="#openFolder" text="Open" />
      
          <TextField fx:id="textfield" layoutX="190.0" layoutY="119.0" prefHeight="23.0" prefWidth="685.0" />
       </children>
    </AnchorPane>
    

    View2.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.layout.HBox?>
    
    
    <HBox alignment="CENTER" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jamesd.examples.switchviews.View2Controller">
       <children>
          <Button mnemonicParsing="false" onAction="#back" text="Back" />
       </children>
    </HBox>
    

    使用模型存储状态的解决方案

    另一种方法是创建包含所需数据的模型,并将文本字段中的文本绑定到模型中的适当属性。然后将相同的模型实例传递给每个控制器

    例如:

    ViewState.java

    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    
    public class ViewState {
    
        private final StringProperty selectedFolder = new SimpleStringProperty();
    
        public final StringProperty selectedFolderProperty() {
            return this.selectedFolder;
        }
        
    
        public final String getSelectedFolder() {
            return this.selectedFolderProperty().get();
        }
        
    
        public final void setSelectedFolder(final String selectedFolder) {
            this.selectedFolderProperty().set(selectedFolder);
        }
        
    }
    

    然后在App.java中:

    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    
    
    public class App extends Application {
    
    
        @Override
        public void start(Stage stage) throws IOException {
            FXMLLoader loader1 = new FXMLLoader(getClass().getResource("View1.fxml"));
            Parent view1 = loader1.load();
            
            
            ViewState viewState = new ViewState();
            
            View1Controller controller = loader1.getController();
            controller.setViewState(viewState);
            
            Scene scene = new Scene(view1);
    
            
            stage.setScene(scene);
            stage.show();
        }
    
    
        public static void main(String[] args) {
            launch();
        }
    
    }
    

    View1Controller.java

    import java.io.File;
    import java.io.IOException;
    
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.control.TextField;
    import javafx.stage.DirectoryChooser;
    import javafx.stage.Stage;
    
    public class View1Controller {
    
        
        @FXML
        private TextField textfield ;
        
    
        private ViewState viewState ;
        
        public void setViewState(ViewState viewState) {
            this.viewState = viewState ;
            textfield.textProperty().bindBidirectional(viewState.selectedFolderProperty());
        }
        
        @FXML
        private void next() throws IOException {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("View2.fxml"));
            Parent root = loader.load();
            textfield.getScene().setRoot(root);
            
            View2Controller controller = loader.getController();
            controller.setViewState(viewState);
        }
        
        @FXML
        public void openFolder(){
            final DirectoryChooser songsOpener = new DirectoryChooser();
            Stage stage = (Stage) textfield.getScene().getWindow();
            File thefolder = songsOpener.showDialog(stage);
            if (thefolder != null){
                textfield.setText(thefolder.getAbsolutePath());
            }
        }
    }
    

    View2Controller

    import java.io.IOException;
    
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    
    public class View2Controller {
        
        private ViewState viewState ;
        
        @FXML
        private Parent root ;
        
        public void setViewState(ViewState viewState) {
            this.viewState = viewState;
        }
        
        @FXML
        private void back() throws IOException {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("View1.fxml"));
            Parent newRoot = loader.load();
            
            this.root.getScene().setRoot(newRoot);
            
            View1Controller controller = loader.getController();
            controller.setViewState(viewState);
        }
    
    }
    

    FXML文件的唯一更改是在第二个视图中向根元素添加fx:id

    View2.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.layout.HBox?>
    
    
    <HBox fx:id="root" alignment="CENTER" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jamesd.examples.switchviews.View2Controller">
       <children>
          <Button mnemonicParsing="false" onAction="#back" text="Back" />
       </children>
    </HBox>