Java 在 JFileChooser 中将选定的文件调整为 FileFilter

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/596429/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 16:40:06  来源:igfitidea点击:

adjust selected File to FileFilter in a JFileChooser

javaswingjfilechooser

提问by amarillion

I'm writing a diagram editor in java. This app has the option to export to various standard image formats such as .jpg, .png etc. When the user clicks File->Export, you get a JFileChooserwhich has a number of FileFilters in it, for .jpg, .pngetc.

我正在用java编写一个图表编辑器。这个程序有选择导出到各种标准图像格式,如为.jpg,.png等。当用户点击文件- >导出,你就会得到一个JFileChooser具有一些FileFilter,它出现,对.jpg.png等等。

Now here is my question:

现在这是我的问题:

Is there a way to have the extension of the default adjust to the selected file filter? E.g. if the document is named "lolcat" then the default option should be "lolcat.png" when the png filter is selected, and when the user selects the jpg file filter, the default should change to "lolcat.jpg" automatically.

有没有办法让默认的扩展名调整到选定的文件过滤器?例如,如果文档名为“lolcat”,则选择 png 过滤器时默认选项应为“lolcat.png”,当用户选择 jpg 文件过滤器时,默认值应自动更改为“lolcat.jpg”。

Is this possible? How can I do it?

这可能吗?我该怎么做?

edit: Based on the answer below, I wrote some code. But it doesn't quite work yet. I've added a propertyChangeListenerto the FILE_FILTER_CHANGED_PROPERTY, but it seems that within this method getSelectedFile()returns null. Here is the code.

编辑:根据下面的答案,我写了一些代码。但它还没有完全奏效。我添加了一个propertyChangeListenerFILE_FILTER_CHANGED_PROPERTY,但似乎这个方法中getSelectedFile()返回null。这是代码。

package nl.helixsoft;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.filechooser.FileFilter;

public class JFileChooserTest {
    public class SimpleFileFilter extends FileFilter {
        private String desc;
        private List<String> extensions;
        private boolean showDirectories;

        /**
         * @param name example: "Data files"
         * @param glob example: "*.txt|*.csv"
         */
        public SimpleFileFilter (String name, String globs) {
            extensions = new ArrayList<String>();
            for (String glob : globs.split("\|")) {
                if (!glob.startsWith("*.")) 
                    throw new IllegalArgumentException("expected list of globs like \"*.txt|*.csv\"");
                // cut off "*"
                // store only lower case (make comparison case insensitive)
                extensions.add (glob.substring(1).toLowerCase());
            }
            desc = name + " (" + globs + ")";
        }

        public SimpleFileFilter(String name, String globs, boolean showDirectories) {
            this(name, globs);
            this.showDirectories = showDirectories;
        }

        @Override
        public boolean accept(File file) {
            if(showDirectories && file.isDirectory()) {
                return true;
            }
            String fileName = file.toString().toLowerCase();

            for (String extension : extensions) {   
                if (fileName.endsWith (extension)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public String getDescription() {
            return desc;
        }

        /**
         * @return includes '.'
         */
        public String getFirstExtension() {
            return extensions.get(0);
        }
    }

    void export() {
        String documentTitle = "lolcat";

        final JFileChooser jfc = new JFileChooser();
        jfc.setDialogTitle("Export");
        jfc.setDialogType(JFileChooser.SAVE_DIALOG);
        jfc.setSelectedFile(new File (documentTitle));
        jfc.addChoosableFileFilter(new SimpleFileFilter("JPEG", "*.jpg"));
        jfc.addChoosableFileFilter(new SimpleFileFilter("PNG", "*.png"));
        jfc.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent arg0) {
                System.out.println ("Property changed");
                String extold = null;
                String extnew = null;
                if (arg0.getOldValue() == null || !(arg0.getOldValue() instanceof SimpleFileFilter)) return;
                if (arg0.getNewValue() == null || !(arg0.getNewValue() instanceof SimpleFileFilter)) return;
                SimpleFileFilter oldValue = ((SimpleFileFilter)arg0.getOldValue());
                SimpleFileFilter newValue = ((SimpleFileFilter)arg0.getNewValue());
                extold = oldValue.getFirstExtension();
                extnew = newValue.getFirstExtension();
                String filename = "" + jfc.getSelectedFile();
                System.out.println ("file: " + filename + " old: " + extold + ", new: " + extnew);
                if (filename.endsWith(extold)) {
                    filename.replace(extold, extnew);
                } else {
                    filename += extnew;
                }
                jfc.setSelectedFile(new File (filename));
            }
        });
        jfc.showDialog(frame, "export");
    }

    JFrame frame;

    void run() {
        frame = new JFrame();
        JButton btn = new JButton ("export");
        frame.add (btn);
        btn.addActionListener (new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                export();
            }
        });
        frame.setSize (300, 300);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {     
            public void run() {
                JFileChooserTest x =  new JFileChooserTest();
                x.run();
            }
        });     
    }
}

采纳答案by Amanda S

It looks like you can listen to the JFileChooserfor a change on the FILE_FILTER_CHANGED_PROPERTYproperty, then change the extension of the selected file appropriately using setSelectedFile().

看起来您可以监听属性JFileChooser的更改FILE_FILTER_CHANGED_PROPERTY,然后使用setSelectedFile().



EDIT: You're right, this solution doesn't work. It turns out that when the file filter is changed, the selected file is removed if its file type doesn't match the new filter. That's why you're getting the nullwhen you try to getSelectedFile().

编辑:你是对的,这个解决方案不起作用。事实证明,当文件过滤器更改时,如果所选文件的文件类型与新过滤器不匹配,则会将其删除。这就是为什么null当您尝试getSelectedFile().

Have you considered adding the extension later? When I am writing a JFileChooser, I usually add the extension after the user has chosen a file to use and clicked "Save":

您是否考虑过稍后添加扩展程序?在编写 . 文件时JFileChooser,我通常在用户选择要使用的文件并单击“保存”后添加扩展名:

if (result == JFileChooser.APPROVE_OPTION)
{
  File file = fileChooser.getSelectedFile();
  String path = file.getAbsolutePath();

  String extension = getExtensionForFilter(fileChooser.getFileFilter());

  if(!path.endsWith(extension))
  {
    file = new File(path + extension);
  }
}


fileChooser.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener()
{
  public void propertyChange(PropertyChangeEvent evt)
  {
    FileFilter filter = (FileFilter)evt.getNewValue();

    String extension = getExtensionForFilter(filter); //write this method or some equivalent

    File selectedFile = fileChooser.getSelectedFile();
    String path = selectedFile.getAbsolutePath();
    path.substring(0, path.lastIndexOf("."));

    fileChooser.setSelectedFile(new File(path + extension));
  }
});

回答by Franck

The use of getAbsolutePath() in the previous change the current directory. I was surprised when the JFileChooser dialog displaying "My documents" directory change to the Netbeans's project directory when I selected a different FileFilter, so I changed it to use getName(). I also used the JDK 6 FileNameExtensionFilter.

前面使用 getAbsolutePath() 改变当前目录。当我选择不同的 FileFilter 时,显示“我的文档”目录的 JFileChooser 对话框更改为 Netbeans 的项目目录时,我感到很惊讶,因此我将其更改为使用 getName()。我还使用了 JDK 6 FileNameExtensionFilter。

Here is the code:

这是代码:

    final JFileChooser fc = new JFileChooser();
    final File sFile = new File("test.xls");
    fc.setSelectedFile(sFile);
    // Store this filter in a variable to be able to select this after adding all FileFilter
    // because addChoosableFileFilter add FileFilter in order in the combo box
    final FileNameExtensionFilter excelFilter = new FileNameExtensionFilter("Excel document (*.xls)", "xls");
    fc.addChoosableFileFilter(excelFilter);
    fc.addChoosableFileFilter(new FileNameExtensionFilter("CSV document (*.csv)", "csv"));
    // Force the excel filter
    fc.setFileFilter(excelFilter);
    // Disable All Files
    fc.setAcceptAllFileFilterUsed(false);

    // debug
    fc.addPropertyChangeListener(new PropertyChangeListener() {

        public void propertyChange(PropertyChangeEvent evt) {
            System.out.println("Property name=" + evt.getPropertyName() + ", oldValue=" + evt.getOldValue() + ", newValue=" + evt.getNewValue());
            System.out.println("getSelectedFile()=" + fc.getSelectedFile());
        }
    });

    fc.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener() {

        public void propertyChange(PropertyChangeEvent evt) {
            Object o = evt.getNewValue();
            if (o instanceof FileNameExtensionFilter) {
                FileNameExtensionFilter filter = (FileNameExtensionFilter) o;

                String ex = filter.getExtensions()[0];

                File selectedFile = fc.getSelectedFile();
                if (selectedFile == null) {
                    selectedFile = sFile;
                }
                String path = selectedFile.getName();
                path = path.substring(0, path.lastIndexOf("."));

                fc.setSelectedFile(new File(path + "." + ex));
            }
        }
    });

回答by Andy Doyle

Here's my attempt at this. It uses the accept() function to check whether or not the file passes the filter. If the filename does not, the extension is appended to the end.

这是我在这方面的尝试。它使用accept() 函数来检查文件是否通过了过滤器。如果文件名不是,则扩展名附加到末尾。

JFileChooser jfc = new JFileChooser(getFile()) {
        public void approveSelection() {
            if (getDialogType() == SAVE_DIALOG) {
                File selectedFile = getSelectedFile();

                FileFilter ff = getFileFilter();

                // Checks against the current selected filter
                if (!ff.accept(selectedFile)) {
                    selectedFile = new File(selectedFile.getPath() + ".txt");
                }
                super.setSelectedFile(selectedFile);

                if ((selectedFile != null) && selectedFile.exists()) {
                    int response = JOptionPane.showConfirmDialog(
                            this,
                            "The file " + selectedFile.getName() + " already exists.\n" +
                            "Do you want to replace it?",
                            "Ovewrite file",
                            JOptionPane.YES_NO_OPTION,
                            JOptionPane.WARNING_MESSAGE
                    );
                    if (response == JOptionPane.NO_OPTION)
                        return;
                }
            }
            super.approveSelection();
        }
    };

回答by Marc Renouf

You can also use a PropertyChangeListener on the SELECTED_FILE_CHANGED_PROPERTY prior to attaching your suffix. When the selected file gets checked against the new filter (and subsequently set to null), the SELECTED_FILE_CHANGED_PROPERTY event is actually fired beforethe FILE_FILTER_CHANGED_PROPERTY event.

在附加后缀之前,您还可以在 SELECTED_FILE_CHANGED_PROPERTY 上使用 PropertyChangeListener。当针对新过滤器检查所选文件(并随后设置为 null)时,SELECTED_FILE_CHANGED_PROPERTY 事件实际上FILE_FILTER_CHANGED_PROPERTY 事件之前触发。

If the evt.getOldValue() != null and the evt.getNewValue() == null, you know that the JFileChooser has blasted your file. You can then grab the old file's name (using ((File)evt.getOldValue()).getName() as described above), pull off the extension using standard string parsing functions, and stash it into a named member variable within your class.

如果 evt.getOldValue() != null 和 evt.getNewValue() == null,您就知道 JFileChooser 已经炸毁了您的文件。然后,您可以获取旧文件的名称(使用 ((File)evt.getOldValue()).getName() 如上所述),使用标准字符串解析函数提取扩展名,并将其存储到类中的命名成员变量中.

That way, when the FILE_FILTER_CHANGED event is triggered (immediately afterwards, as near as I can determine), you can pull that stashed root name from the named member variable, apply the extension for the new file filter type, and set the JFileChooser's selected file accordingly.

这样,当 FILE_FILTER_CHANGED 事件被触发时(紧接着,我可以确定),您可以从命名成员变量中提取隐藏的根名称,应用新文件过滤器类型的扩展名,并设置 JFileChooser 的选定文件因此。

回答by Clandestino

How about this:

这个怎么样:

class MyFileChooser extends JFileChooser {
   public void setFileFilter(FileFilter filter) {

    super.setFileFilter(filter);

    FileChooserUI ui = getUI();

    if( ui instanceof BasicFileChooserUI ) {
     BasicFileChooserUI bui = (BasicFileChooserUI) ui;

     String file = bui.getFileName();

     if( file != null ) {
      String newFileName = ... change extension 
      bui.setFileName( newFileName );
     }
    }
   }
  }

回答by Tika

Here is my solution and it works fine. It maybe helps someone. You sould create your own "MyExtensionFileFilter" class, otherwise you have to modify the code.

这是我的解决方案,它工作正常。它可能会帮助某人。您应该创建自己的“MyExtensionFileFilter”类,否则您必须修改代码。

public class MyFileChooser extends JFileChooser {
    private File file = new File("");

    public MyFileChooser() {
        addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent e) {
                String filename = MyFileChooser.this.file.getName();
                String extold = null;
                String extnew = null;
                if (e.getOldValue() == null || !(e.getOldValue() instanceof MyExtensionFileFilter)) {
                    return;
                }
                if (e.getNewValue() == null || !(e.getNewValue() instanceof MyExtensionFileFilter)) {
                    return;
                }
                MyExtensionFileFilter oldValue = ((MyExtensionFileFilter) e.getOldValue());
                MyExtensionFileFilter newValue = ((MyExtensionFileFilter) e.getNewValue());
                extold = oldValue.getExtension();
                extnew = newValue.getExtension();

                if (filename.endsWith(extold)) {
                    filename = filename.replace(extold, extnew);
                } else {
                    filename += ("." + extnew);
                }
                setSelectedFile(new File(filename));
            }
        });
    }

    @Override
    public void setSelectedFile(File file) {
        super.setSelectedFile(file);
        if(getDialogType() == SAVE_DIALOG) {
            if(file != null) {
                super.setSelectedFile(file);
                this.file = file;
            }
        }
    }

    @Override
    public void approveSelection() { 
        if(getDialogType() == SAVE_DIALOG) {
            File f = getSelectedFile();  
            if (f.exists()) {  
                String msg = "File existes ...";  
                msg = MessageFormat.format(msg, new Object[] { f.getName() });  
                int option = JOptionPane.showConfirmDialog(this, msg, "", JOptionPane.YES_NO_OPTION);
                if (option == JOptionPane.NO_OPTION ) {  
                    return;  
                }
            }
        }
        super.approveSelection();   
    }

    @Override
    public void setVisible(boolean visible) {
        super.setVisible(visible);
        if(!visible) {
            resetChoosableFileFilters();
        }
    }
}

回答by amicus99

Here is a method to obtain the current file name (as a String). In your property change listener for JFileChooser.FILE_FILTER_CHANGED_PROPERTY, you make the following call:

这是一种获取当前文件名(作为字符串)的方法。在您的 属性更改侦听器中JFileChooser.FILE_FILTER_CHANGED_PROPERTY,您进行以下调用:

final JFileChooser fileChooser = new JFileChooser();
fileChooser.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener()
{
    @Override
    public void propertyChange(PropertyChangeEvent e) {
        String currentName = ((BasicFileChooserUI)fileChooser.getUI()).getFileName();
        MyFileFilter filter = (MyFileFilter) e.getNewValue();

        // ... Transform currentName as you see fit using the newly selected filter.
        // Suppose the result is in newName ...

        fileChooser.setSelectedFile(new File(newName));
    }
});

The getFileName()method of javax.swing.plaf.basic.BasicFileChooserUI(the descendant of FileChooserUIreturned by JFileChooser.getUI()) will return the contents of the dialog's text box that is used to type in the file name. It seems that this value is always set to a non-null String (it returns an empty string if the box is empty). On the other hand, getSelectedFile()returns null if the user has not selected an existing file yet.

getFileName()方法javax.swing.plaf.basic.BasicFileChooserUI(的后代FileChooserUI通过返回JFileChooser.getUI())将返回所使用的文件名输入对话框的文本框的内容。似乎此值始终设置为非空字符串(如果框为空,则返回空字符串)。另一方面,getSelectedFile()如果用户尚未选择现有文件,则返回 null。

It seems that the dialog's design is governed by the 'file selection' concept; that is, while the dialog is visible getSelectedFile()only returns a meaningful value if the user has already selected an existing file or the program called setSelectedFile(). getSelectedFile()will return what the user typed in afterthe user clicks the approve (i.e. OK) button.

对话框的设计似乎受“文件选择”概念的支配;也就是说,当对话框可见时,getSelectedFile()如果用户已经选择了一个现有文件或程序,则仅返回一个有意义的值setSelectedFile()getSelectedFile()将在用户单击批准(即确定)按钮返回用户输入的内容。

The technique will only work for single-selection dialogs, however changing file extension based on selected filter should also make sense for single files only ("Save As..." dialogs or similar).

该技术仅适用于单选对话框,但是根据选定的过滤器更改文件扩展名也应该仅适用于单个文件(“另存为...”对话框或类似对话框)。

This design was a subject of a debate at sun.com back in 2003, see the linkfor details.

早在 2003 年,这种设计就成为 sun.com 上的一个辩论主题,请参阅链接了解详细信息。