问题
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
}
}
}
回答3:
Modified above code to show in selection order in the text field
/*
* 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();
}
}
}
来源:https://stackoverflow.com/questions/29284814/jfilechooser-open-multiple-files-in-the-order-they-are-clicked