JFileChooser 按点击顺序打开多个文件

JFileChooser open multiple files in the order they are clicked

我有一个应用程序需要多个文件并根据它们的顺序应用一些操作(例如,一个接一个地合并它们)。

用户可以通过 Ctrl+单击或 Shift+单击按任意顺序select 文件。

选择器返回的列表文件与用户单击它们的顺序不同。我希望它们按照用户点击它们的相同顺序返回。

免责声明:我是 "the user"

我在 Windows 7 64 位上使用具有 Java 外观的 JFileChooser class,JDK 7.

这是一个最小的例子

package choosertest;

import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.JFrame;

public class ChooserTest extends JFrame {

    JFileChooser chooser;

    public ChooserTest() {
        chooser = new JFileChooser();
        chooser.setMultiSelectionEnabled(true);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        testOpen();
    }

    public static void main(String[] args) {
        new ChooserTest();
    }

    private void testOpen() {
        int choice = chooser.showOpenDialog(this);

        if (choice == JFileChooser.APPROVE_OPTION) {

            File[] inputFiles = chooser.getSelectedFiles();
            for (File f: inputFiles) {
                System.out.println(f.getName());
            }
        }
    }

}

与其根据 JFileChooser 中的选择顺序对文件进行排序,不如考虑在选择后使用类似 JTable/JList 的方式对文件重新排序。尽管如此,您可以通过向 JFileChooser 添加 PropertyChangeListener 来插入 JFileChooser 的侦听器系统以获取选择顺序。 Whenever a File is selected, the 'SelectedFilesChangedProperty' is fired, and PropertyChangeEvent.getNewValue() should return the selected files

    final JFileChooser chooser = new JFileChooser();
    chooser.setMultiSelectionEnabled(true);
    chooser.addPropertyChangeListener(new PropertyChangeListener(){

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if ( evt.getPropertyName().equals("SelectedFilesChangedProperty") ){
                System.out.println(Arrays.toString((File[])evt.getNewValue()));
            }
        }
    });

您需要检查由 getNewValue() return 编辑的文件数组,以查看选择(或取消选择)了哪个新文件以维持顺序。

我说到 Ctrl+click 有效,Shift+click selecting files "downwards" 有效。

但是,使用 Shift 键并单击 select 个文件 "upwards" 仍然会以错误的顺序添加文件,这可能会造成混淆。

此外,此解决方案不会更新 "File name" 文本字段以反映 selection 的实际顺序。不过,可能 "fix" 使用反射来做到这一点。

如果您需要按正确顺序快速解决 select 文件,方法是按住 Ctrl 键并单击它们,这很好用(select 和 deselect)。

package choosertest;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JFrame;

public class ChooserTest extends JFrame {

    File[] selected;
    JFileChooser chooser;

    public ChooserTest() {
        chooser = new JFileChooser();
        chooser.setMultiSelectionEnabled(true);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);

        testOpen();
    }

    public static void main(String[] args) {
        new ChooserTest();
    }

    private void testOpen() {

        chooser.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getPropertyName().equals("SelectedFilesChangedProperty")) {
                    if (selected == null) {
                        selected = (File[]) evt.getNewValue();
                    } else {
                        File[] newSelection = (File[]) evt.getNewValue();

                        if (newSelection == null) {
                            selected = null;
                        }
                        // check back and forth to preserve the order of files
                        // as the user added them
                        else {
                            List<File> orderedSel = new LinkedList<>();

                            // add files that are still selected
                            for (File f : selected) {
                                for (File f2 : newSelection) {
                                    if (f.equals(f2)) {
                                        orderedSel.add(f);
                                        break;
                                    }
                                }
                            }

                            Arrays.sort(selected);
                            // add newly selected files
                            for (File f : newSelection) {
                                if (Arrays.binarySearch(selected, f) < 0) {
                                    orderedSel.add(f);
                                }
                            }

                            selected = orderedSel.toArray(
                                    new File[orderedSel.size()]);
                        }
                    }
                    System.out.println(Arrays.toString(selected)); //debug
                }
            }
        });

        // copy previous array of selected files
        File[] prevSelected = null;
        if (selected != null) {
            prevSelected = new File[selected.length];
            System.arraycopy( selected, 0, prevSelected, 0, selected.length );
        }

        int choice = chooser.showOpenDialog(this);

        // if the user did not cancel the selection
        if (choice == JFileChooser.APPROVE_OPTION) {
            System.out.println("FINAL selection: " + Arrays.toString(selected)); //debug
        } else {
            // restore the previous selection
            selected = prevSelected;
            System.out.println("PREVIOUS selection: " + Arrays.toString(selected)); //debug
        }
    }

}

修改以上代码以在文本字段中按选择顺序显示

/*
 * FileName  :SelectedOrderedFileChooser.java
 */

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFileChooser;
import javax.swing.JTextField;
import javax.swing.plaf.metal.MetalFileChooserUI;

/**
 * File Chooser with selection order<br>
 * Only work for Ctrl + click selection
 */
public class SelectedOrderedFileChooser extends JFileChooser{

    /** Selection order file name list */
    private List<String> selectedFileNames = new ArrayList<String>();

    boolean isSelectionOrder = false;

    /**
     * Constructor
     */
    public SelectedOrderedFileChooser() {
        super();
        setSelectionOrder(false);
    }

    /**
     * Constructor
     * @param aIsSelectionOrder Selection ordered or not<br>
     * true : Selection order, false : File Name order(JFileChooser Default)
     */
    public SelectedOrderedFileChooser(boolean aIsSelectionOrder) {
        super();
        setSelectionOrder(aIsSelectionOrder);
    }
    /**
     * Get selected file names in selection order
     * @param fs Selected files
     * @return File names in selection order
     */
    private List<String> getFileNamesInSelectionOrder(File[] fs) {
        List<String> tmpSelectedFileNames = new ArrayList<String>();
        List<String> tmpResultFileNames = new ArrayList<String>();
        for (File f : fs) {
            // Get all current selected file names
            tmpSelectedFileNames.add(f.getAbsolutePath());
        }
        for (String fn : selectedFileNames) {
            if (tmpSelectedFileNames.contains(fn)) {
                // If ordered selected file name is in current selected file names, add to temporary file name list
                tmpResultFileNames.add(fn);
            }
        }

        // If there are current selected file names that is not in temporary file name list, add them
        for (String fn : tmpSelectedFileNames) {
            if (tmpResultFileNames.contains(fn) == false) {

                tmpResultFileNames.add(fn);
            }
        }
        return tmpResultFileNames;
    }

    /**
     * Get selected file names
     * @return Selected file names
     */
    public List<String> getSelectedFileNames() {
        return selectedFileNames;
    }

    /**
     * Is selection order or not?
     * @return true : Selection order, false : File Name order(JFileChooser Default)
     */
    public boolean isSelectionOrder() {
        return isSelectionOrder;
    }

    /**
     * Set whether file selection order or not.
     * @param aIsSelectionOrder true : Selection order, false : File Name order(JFileChooser Default)
     */
    public void setSelectionOrder(boolean aIsSelectionOrder) {
        this.isSelectionOrder = aIsSelectionOrder;
        this.addPropertyChangeListener(new PropertyChangeListener(){

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if ( evt.getPropertyName().equals("SelectedFilesChangedProperty") ){
                    File[] fs = (File[])evt.getNewValue();
                    if (null != fs && fs.length > 0) {
                        if (isSelectionOrder) {
                             // If selection order、get file name in selection order and set to selected order file name list
                            List<String> tmpResultFileNames = getFileNamesInSelectionOrder(fs);
                            selectedFileNames = tmpResultFileNames;
                            showTextField();
                        } else {
                            // If not selection order、FileChooser default order
                            selectedFileNames.clear();
                            for (File f : fs) {
                                selectedFileNames.add(f.getAbsolutePath());
                            }
                        }
                    }
                }
            }
        });
    }

    /**
     * Get selection ordered files in array
     * @return selection ordered files in array
     */
    public File[] getSelectedOrderedFiles() {
        List<File> ret = new ArrayList<File>();
        for (String fName : selectedFileNames) {
            File f = new File(fName);
            ret.add(f);
        }
        return ret.toArray(new File[ret.size()]);
    }

    /**
     * Show file names in selection order in text field of JFileChooser
     */
    public void showTextField(){
        try {
            String res = "";
            for (File f : getSelectedOrderedFiles()) {
                res += "\"" + f.getName() + "\" ";
            }
            MetalFileChooserUI ui = (MetalFileChooserUI)this.getUI();
            Field field;
            field = MetalFileChooserUI.class.getDeclaredField("fileNameTextField");
            field.setAccessible(true);
            JTextField tf = (JTextField) field.get(ui);
            tf.setText(res.trim());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}