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

时间:2016-04-28 16:13:44

标签: java javafx javafx-8 filechooser

我的视图控制器有一个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);
    }

}

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

4 个答案:

答案 0 :(得分:4)

  

如何使对话框的“当前目录”保持不变   不同的调用?

您可以修改此{/ 3>的Singleton Pattern方法

因此你只能使用一个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"));

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

改善这一点的一种方法是在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);
  

但如果关闭或取消对话框,则无效。

不幸的是,FileChooser在关闭/终止之前不会公开有关正在检查的目录的信息。如果您对类进​​行反编译并进行跟踪,则最终会发出native次调用。因此,即使FileChooser不是final允许它被子类化,也不太可能获得此信息

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

如果您有兴趣,可以在此问题中的native关键字以及相关链接上找到一些非常好的答案:

答案 1 :(得分:3)

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

答案 2 :(得分:0)

也许也很有趣...

如果您要保存关闭应用程序后选择的文件,并在再次启动应用程序后将其再次用作初始文件值(并且您没有太多要存储的东西),您可能会倒下回到Windows的旧.ini文件。

ini4j是这方面的一个好帮手。它具有您所需的基本内容。清晰,简单且无麻烦。

答案 3 :(得分:0)

我的建议是,创建一个不可附加的日志文件,并使用FileReaders和Writers创建一个writer类,这样,每次打开文件后,一旦成功选择FileChooser都将其关闭,您将最后访问的目录的绝对路径写入日志文件。

创建一个读取器方法,该方法从文件中返回路径。每当您打开FileChooser用户时,返回的读取值将作为方法setInitialDirectory(the-read-path)的值

这将确保每当您重新打开FileChooser时,即使取消了最后一个实例,它也将打开成功的记录条目。我发现非附加文件的文件读取器和写入器速度更快。如果适合您的情况,您可以尝试此方法。