我的视图控制器有一个FileChooser实例,用于打开和保存文件.
每次我从该实例调用showOpenDialog()或showSaveDialog()时,我希望生成的对话框与我上次调用其中一个时离开它时在同一目录中.
相反,每次我调用其中一个方法时,对话框都会在用户主目录中打开.
如何使对话框的“当前目录”在不同的调用中保持不变?
当前行为的示例:
import java.io.File; import javafx.application.Application; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import javafx.stage.Stage; /** * Demonstrates the use of an open dialog. * * @author N99x */ public class FileChooserTest extends Application { private final FileChooser open = new FileChooser(); private File lastOpened = null; @Override public void start(Stage primaryStage) { Label lbl = new Label("File Opened: <null>"); lbl.setPadding(new Insets(8)); Button btn = new Button(); btn.setPadding(new Insets(8)); btn.setText("Open"); btn.setOnAction((ActionEvent event) -> { open.setInitialDirectory(lastOpened); File selected = open.showOpenDialog(primaryStage); if (selected == null) { lbl.setText("File Opened: <null>"); // lastOpened = ??; } else { lbl.setText("File Opened: " + selected.getAbsolutePath()); lastOpened = selected.getParentFile(); } }); VBox root = new VBox(lbl,btn); root.setPadding(new Insets(8)); root.setAlignment(Pos.TOP_CENTER); Scene scene = new Scene(root,300,300); primaryStage.setTitle("FileChooser Testing!"); primaryStage.setScene(scene); primaryStage.show(); } /** * @param args the command line arguments */ public static void main(String[] args) { launch(args); } }
解决方法
How do I make the “current directory” of the dialogs persist across
different invocations?
因此,您只能使用一个FileChooser并监视/控制那里的初始目录,但不会直接将该实例暴露给类外的修改
例如:
public class RetentionFileChooser { private static FileChooser instance = null; private static SimpleObjectProperty<File> lastKnownDirectoryProperty = new SimpleObjectProperty<>(); private RetentionFileChooser(){ } private static FileChooser getInstance(){ if(instance == null) { instance = new FileChooser(); instance.initialDirectoryProperty().bindBidirectional(lastKnownDirectoryProperty); //Set the FileExtensions you want to allow instance.getExtensionFilters().setAll(new ExtensionFilter("png files (*.png)","*.png")); } return instance; } public static File showOpenDialog(){ return showOpenDialog(null); } public static File showOpenDialog(Window ownerWindow){ File chosenFile = getInstance().showOpenDialog(ownerWindow); if(chosenFile != null){ //Set the property to the directory of the chosenFile so the fileChooser will open here next lastKnownDirectoryProperty.setValue(chosenFile.getParentFile()); } return chosenFile; } public static File showSaveDialog(){ return showSaveDialog(null); } public static File showSaveDialog(Window ownerWindow){ File chosenFile = getInstance().showSaveDialog(ownerWindow); if(chosenFile != null){ //Set the property to the directory of the chosenFile so the fileChooser will open here next lastKnownDirectoryProperty.setValue(chosenFile.getParentFile()); } return chosenFile; } }
这将把FileChooser的初始目录设置为用户上次重新打开/保存的文件的目录
用法示例:
File chosenFile = RetentionFileChooser.showOpenDialog();
但是这里有一个限制:
//Set the FileExtensions you want to allow instance.getExtensionFilters().setAll(new ExtensionFilter("png files (*.png)","*.png"));
由于没有提供任何ExtensionFilter,FileChooser变得不那么用户友好,要求用户手动附加文件类型,但在创建实例时提供过滤器而无法更新它们将其限制为相同的过滤器
改进这一点的一种方法是在RetentionFileChooser中公开可以使用varargs提供的可选过滤器,从而可以在显示对话框时选择修改过滤器的时间
例如,建立在前一个:
public class RetentionFileChooser { public enum FilterMode { //Setup supported filters PNG_FILES("png files (*.png)","*.png"),TXT_FILES("txt files (*.txt)","*.txt"); private ExtensionFilter extensionFilter; FilterMode(String extensionDisplayName,String... extensions){ extensionFilter = new ExtensionFilter(extensionDisplayName,extensions); } public ExtensionFilter getExtensionFilter(){ return extensionFilter; } } private static FileChooser instance = null; private static SimpleObjectProperty<File> lastKnownDirectoryProperty = new SimpleObjectProperty<>(); private RetentionFileChooser(){ } private static FileChooser getInstance(FilterMode... filterModes){ if(instance == null) { instance = new FileChooser(); instance.initialDirectoryProperty().bindBidirectional(lastKnownDirectoryProperty); } //Set the filters to those provided //You could add check's to ensure that a default filter is included,adding it if need be instance.getExtensionFilters().setAll( Arrays.stream(filterModes) .map(FilterMode::getExtensionFilter) .collect(Collectors.toList())); return instance; } public static File showOpenDialog(FilterMode... filterModes){ return showOpenDialog(null,filterModes); } public static File showOpenDialog(Window ownerWindow,FilterMode...filterModes){ File chosenFile = getInstance(filterModes).showOpenDialog(ownerWindow); if(chosenFile != null){ lastKnownDirectoryProperty.setValue(chosenFile.getParentFile()); } return chosenFile; } public static File showSaveDialog(FilterMode... filterModes){ return showSaveDialog(null,filterModes); } public static File showSaveDialog(Window ownerWindow,FilterMode... filterModes){ File chosenFile = getInstance(filterModes).showSaveDialog(ownerWindow); if(chosenFile != null){ lastKnownDirectoryProperty.setValue(chosenFile.getParentFile()); } return chosenFile; } }
用法示例:
//Note the prevIoUs example still holds File chosenFile = RetentionFileChooser.showOpenDialog(); File file = RetentionFileChooser.showSaveDialog(FilterMode.PNG_FILES);
but this does not work if the dialog is closed or canceled.
遗憾的是,FileChooser在关闭/终止之前不会公开有关正在检查的目录的信息.如果您对类进行反编译并进行跟踪,则最终会触发本机调用.因此即使FileChooser不是最终允许它被子类化,它也不太可能获得这些信息
上述方法提供的唯一好处是,如果遇到此情况,它不会更改初始目录
如果你有兴趣,这个问题中的本地关键词有一些非常好的答案和链接: