JFileChooser open multiple files in the order they

2019-07-28 14:53发布

问题:

I have an application that takes multiple files and applies some operation that depends on their order (e.g. merge them one after another).

The user can select files in any order by Ctrl+click, or by Shift+click.

The list files returned by the chooser does not have the same order as the user clicked them. I'd like them to be returned in the same order the user clicked them.

Disclaimer: I'm "the user"

I'm using the JFileChooser class with Java look and feel on Windows 7 64bits, with JDK 7.

Here's a minimal example

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

}

回答1:

Rather than ordering the files based upon selection order in a JFileChooser, you might consider re-ordering the files after selection using something like a JTable/JList. Be that as it may, you might be able to plug into the listener system of a JFileChooser to get the selection order by adding a PropertyChangeListener to the 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()));
            }
        }
    });

You would need to check the array of Files returned by getNewValue() to see which new file was selected (or deselected) to maintain order.



回答2:

I got to the point that it Ctrl+click works and Shift+click selecting files "downwards" works.

However, using Shift+click to select files "upwards" still adds files in the wrong order, which can be confusing.

Moreover, this solution does not update the "File name" text field to reflect the actual order of the selection. It may be possible to "fix" this using reflection, though.

If you need a quick solution to select files in the correct order by Ctrl-clicking them, this works just fine (both select and 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
        }
    }

}