настроить выбранный файл на FileFilter в JFileChooser

Пишу редактор диаграмм на java. Это приложение имеет возможность экспортировать изображения в различные стандартные форматы изображений, такие как .jpg, .png и т. Д. Когда пользователь нажимает File-> Export, вы получаете a, в JFileChooser котором есть несколько FileFilter s, for .jpg и .png т. Д.

Вот мой вопрос:

Есть ли способ настроить расширение по умолчанию для выбранного фильтра файлов? Например, если документ назван «lolcat», то параметр по умолчанию должен быть «lolcat.png», когда выбран фильтр png, а когда пользователь выбирает фильтр файла jpg, значение по умолчанию должно измениться на «lolcat.jpg» автоматически.

Это возможно? Как я могу это сделать?

изменить: основываясь на приведенном ниже ответе, я написал код. Но это пока не совсем работает. Я добавил propertyChangeListener к FILE_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();
            }
        });     
    }
}

Ответов (8)

Решение

Похоже, вы можете прослушать JFileChooser изменение FILE_FILTER_CHANGED_PROPERTY свойства, а затем соответствующим образом изменить расширение выбранного файла, используя setSelectedFile() .


РЕДАКТИРОВАТЬ: Вы правы, это решение не работает. Оказывается, при изменении фильтра файлов выбранный файл удаляется, если его тип файла не соответствует новому фильтру. Вот почему вы получаете то, null когда пытаетесь getSelectedFile() .

Думали ли вы о добавлении расширения позже? Когда я пишу 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));
  }
});

Вот мое решение, и оно отлично работает. Возможно, это кому-то поможет. Вы можете создать свой собственный класс «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();
        }
    }
}

Вот способ получить текущее имя файла (в виде строки). В вашем слушателе изменения свойства 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));
    }
});

getFileName() Метод javax.swing.plaf.basic.BasicFileChooserUI (потомок FileChooserUI возвращаемого JFileChooser.getUI() ) возвращает содержимое текстового поля диалогового окна, которая используется для ввода имени файла. Кажется, что это значение всегда установлено в ненулевую строку (она возвращает пустую строку, если поле пусто). С другой стороны, getSelectedFile() возвращает null, если пользователь еще не выбрал существующий файл.

Кажется, что дизайн диалога определяется концепцией «выбора файла»; то есть, пока диалоговое окно является видимым, 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).

Этот дизайн был предметом обсуждения на sun.com еще в 2003 году, подробности см. По ссылке .

Я рекомендую использовать FileNameExtensionFilter вместо этого, FileFilter если ваша версия Java поддерживает его. В противном случае создайте свой собственный новый аналогичный абстрактный класс, который расширяется 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() вы можете просто использовать первую запись в массиве.

Использование getAbsolutePath () в предыдущем случае изменяет текущий каталог. Я был удивлен, когда диалоговое окно JFileChooser, отображающее каталог «Мои документы», изменилось на каталог проекта Netbeans, когда я выбрал другой FileFilter, поэтому я изменил его на использование 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));
            }
        }
    });

Вот моя попытка. Он использует функцию 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();
        }
    };

Вы также можете использовать PropertyChangeListener для SELECTED_FILE_CHANGED_PROPERTY перед добавлением суффикса. Когда выбранный файл проверяется на соответствие новому фильтру (и впоследствии устанавливается значение null), событие SELECTED_FILE_CHANGED_PROPERTY фактически запускается перед событием FILE_FILTER_CHANGED_PROPERTY.

Если evt.getOldValue ()! = Null и evt.getNewValue () == null, вы знаете, что JFileChooser взорвал ваш файл. Затем вы можете получить имя старого файла (используя ((File) evt.getOldValue ()). GetName (), как описано выше), извлечь расширение, используя стандартные функции синтаксического анализа строк, и спрятать его в именованную переменную-член в вашем классе. .

Таким образом, когда запускается событие FILE_FILTER_CHANGED (сразу после этого, насколько я могу определить), вы можете извлечь это сохраненное корневое имя из именованной переменной-члена, применить расширение для нового типа фильтра файлов и установить выбранный файл JFileChooser соответственно.

Как насчет этого:

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