在JFileChooser中,调整选定文件以适应FileFilter。

15
我正在用Java编写一个图表编辑器。这个应用程序可以将图表导出为各种标准图像格式,如.jpg、.png等。当用户点击文件->导出时,会得到一个JFileChooser,其中有许多FileFilter,用于过滤.jpg、.png等格式的文件。
现在我的问题是:是否有办法使默认扩展名根据所选的文件过滤器进行调整?例如,如果文档命名为"lolcat",则选择png过滤器时默认选项应为"lolcat.png",当用户选择jpg文件过滤器时,默认选项应自动更改为"lolcat.jpg"。
这是否可行?我该如何做?
编辑: 基于下面的答案,我编写了一些代码。但它还不太能正常工作。我已经向FILE_FILTER_CHANGED_PROPERTY添加了propertyChangeListener,但似乎在此方法中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();
            }
        });     
    }
}
8个回答

13

看起来你可以监听JFileChooserFILE_FILTER_CHANGED_PROPERTY属性的变化,然后使用setSelectedFile()适当地更改所选文件的扩展名。


编辑:你是对的,这种解决方案不起作用。事实证明,当文件筛选器更改时,如果所选文件类型与新的筛选器不匹配,则所选文件将被删除。这就是为什么在尝试getSelectedFile()时会返回null的原因。

你考虑过稍后添加扩展名吗?当我编写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));
  }
});

5
这个怎么样:
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 );
     }
    }
   }
  }

5

在附加后缀之前,您还可以使用SELECTED_FILE_CHANGED_PROPERTY上的PropertyChangeListener。当选定的文件与新过滤器进行检查(并随后设置为null)时,实际上会触发SELECTED_FILE_CHANGED_PROPERTY事件,而不是FILE_FILTER_CHANGED_PROPERTY事件。

如果evt.getOldValue()!= null且evt.getNewValue()== null,则表示JFileChooser已经删除了您的文件。然后,您可以使用上面描述的((File)evt.getOldValue()).getName()获取旧文件的名称,使用标准字符串解析功能去掉扩展名,并将其存储到类内的命名成员变量中。

这样,当FILE_FILTER_CHANGED事件被触发(似乎是紧接着之后),您就可以从命名成员变量中提取该存储的根名称,应用新文件过滤器类型的扩展名,并相应地设置JFileChooser的选定文件。


4

以下是我的解决方案,它工作得很好。它可能会帮助到某些人。你应该创建自己的"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();
        }
    }
}

4

以下是一种获取当前文件名(作为字符串)的方法。在您的属性更改监听器中,监听 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));
    }
});
javax.swing.plaf.basic.BasicFileChooserUIgetFileName() 方法(JFileChooser.getUI() 返回的 FileChooserUI 的后代)将返回用于键入文件名的对话框文本框的内容。似乎这个值总是设置为非空字符串(如果该框为空,则返回空字符串)。另一方面,如果用户尚未选择现有文件,则 getSelectedFile() 返回 null。
看起来,对话框的设计是由“文件选择”概念控制的。也就是说,在对话框可见时,getSelectedFile() 只有在用户已经选择了现有文件或程序调用了 setSelectedFile() 时才会返回有意义的值。 getSelectedFile() 将返回用户在单击“确定”(即 OK)按钮后键入的内容。
但是,这种技术仅适用于单选对话框,但根据所选过滤器更改文件扩展名也应仅对单个文件(例如“另存为…”对话框)有意义。
这种设计曾在2003年成为sun.com上的一个争论主题,请参见链接获取详细信息。

0
这是我的尝试。它使用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();
        }
    };

0
在之前的更改中使用getAbsolutePath()会改变当前目录。当我选择不同的FileFilter时,JFileChooser对话框显示“我的文档”目录改变为Netbeans项目目录时,我感到惊讶,因此我将其更改为使用getName()。我还使用了JDK 6 FileNameExtensionFilter。
以下是代码:
    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));
            }
        }
    });

0

如果你的Java版本支持,我建议使用FileNameExtensionFilter而不是FileFilter。否则,创建一个新的类,它类似于FileFilter并且继承自它,并添加一个getExtension方法(类似于FileNameExtensionFilter.getExtensions)。然后为每个你打算使用的导出过滤器重写getExtension方法。

public abstract class MyFileFilter extends FileFilter {
    abstract public String getExtension();
}

那么,对于一个示例JPG过滤器,您只需要比以前多覆盖一个方法:

    MyFileFilter filterJPG = new MyFileFilter () {
        @Override
        public String getDescription() {
            return "A JPEG image (*." + getExtension() + ")";
        }
        @Override
        public boolean accept(File f) {
            String filename = f.getName().toLowerCase();
            return filename.endsWith("."+getExtension());
        }
        @Override
        public String getExtension() { return "jpg"; }
    };

用户选择文件后,只需调用getFileFilter来确定用户选择了哪个过滤器:

jfc.showDialog(frame, "export");
File file = jfc.getSelectedFile();
RvFileFilter filter = (RvFileFilter)jfc.getFileFilter();
String sExt = filter.getExtension(); //The extension to ensure the file has

如果您能够使用FileNameExtensionFilter.getExtensions(),那么您可以简单地使用数组中的第一个条目。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接