JavaFX FileChooser可以“记住”它打开的最后一个目录吗?

2022-09-04 21:38:52

我的视图控制器有一个用于打开和保存文件的实例。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);
    }

}

我设法通过存储打开的值解决了部分问题,但是如果对话框关闭或取消,这将不起作用。


答案 1

如何使对话框的“当前目录”在不同的调用中持久存在?

您可以为此修改单例模式方法

这样,您只能使用一个并监视/控制那里的初始目录,但不会直接将实例暴露给类外部的修改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"));

由于不提供任何 's 变得不那么用户友好,要求用户手动附加文件类型,但在创建实例时提供过滤器而无法更新它们会将其限制为相同的过滤器ExtensionFilterFileChooser

对此进行改进的一种方法是公开可以使用 varargs 提供的可选筛选器,从而可以选择在显示对话框时修改筛选器的时间RetentionFileChooser

例如,在上一个的基础上构建:

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);

但如果对话框关闭或取消,则此操作不起作用。

不幸的是,在关闭/终止目录之前,不会公开有关正在检查的目录的信息。如果对类进行反编译并跟踪,它最终会命中一个调用。因此,即使不允许它被子类化,它也不太可能获得这些信息。FileChoosernativeFileChooserfinal

上述方法在这方面提供的唯一好处是,如果遇到这种情况,它不会更改初始目录


如果您有兴趣,那么在这个问题和链接的关键词上有一些非常好的答案:native


答案 2
private static String lastVisitedDirectory=System.getProperty("user.home");

FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("View Pictures");
fileChooser.setInitialDirectory(new  File(lastVisitedDirectory));
fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("--"));

List<File> files = fileChooser.showOpenMultipleDialog(----);


lastVisitedDirectory=(files!=null && files.size()>=1)?files.get(0).getParent():System.getProperty("user.home"); 

推荐