如何在 Java 中的自定义 JFrame 中附加 JFileChooser 按钮的处理程序

How to attach handler for JFileChooser buttons inside custom JFrame in Java

我有一个名为 MyFileChooser 的单例模式 class,如果 JFileChooser 是 FILES_ONLYDIRECTORIES_ONLY,它会根据构造函数参数进行调整。

我希望在 JFrame 中包含 JFileChooser,这样我就可以在 JFileChooser 框架的上方和下方添加附加信息,因此结构如下所示:

JFrame
 | JLabel
 | JFileChooser
 | JLabel
// end of JFrame

import javax.swing.JFrame;
import javax.swing.JFileChooser;

public class MyFileChooser {
    private JFrame frame;
    private boolean isFilesOnly;
    private static final MyFileChooser instance_files = new MyFileChooser( true );
    private static final MyFileChooser instance_dirs = new MyFileChooser( false );

    private JFileChooser dynamicChooser;

    private MyFileChooser( boolean filesOnly ) {
        this.frame = new JFrame();
        this.isFilesOnly = filesOnly;
        this.dynamicChooser = new JFileChooser();
        this.frameSetup();
        this.chooserSetup();
    }

    public MyFileChooser getInstance( boolean filesOnly ) {
        if ( filesOnly ) {
            return MyFileChooser.instance_files;
        } else {
            return MyFileChooser.instance_dirs;
        }
    }

    public void frameSetup() {
        // jframe and labels setup code
        this.frame.getContentPane().add( this.dynamicChooser );
    }

    public void chooserSetup() {
        if ( this.isFilesOnly ) {
            this.dynamicChooser.setFileSelectionMode( JFileChooser.FILES_ONLY );
        } else {
            this.dynamicChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY );
        }
        this.dynamicChooser.setMultiSelectionEnabled(true);
        this.dynamicChooser.setDialogType(JFileChooser.CUSTOM_DIALOG);
    }

问题是我不知道如何为 JFileChooser 的 "Close" 和 "Open" 按钮附加处理程序。我唯一发现的是:

public void handleSelectedFiles() {
   int returnVal = this.dynamicChooser.showDialog(this.frame, "Open");
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File[] files = this.dynamicChooser.getSelectedFiles();
            // do something
        }
}

但是,如果调用此方法,即使没有来自所需结构的 JFrame 包装和 JLabel,它也会打开这两个实例。 所以我的问题是如何从 JFileChooser 获取 "Open" 和 "Close" 的按钮以附加处理程序( 实例不同 )或如何处理其他选择的文件方式。

你的第一个问题就在这里

int returnVal = this.dynamicChooser.showDialog(this.frame, "Open");

它调用底层 JFileChoosershowDialog 方法,因此它正在构建自己的 window,忽略你的

更好的解决方案是按需创建 JDialog 并将其用于包含文件选择器和其他控件

有点像...

public class MyFileChooser {

    private final boolean isFilesOnly;
    private static final MyFileChooser INSTANCE_FILES = new MyFileChooser(true);
    private static final MyFileChooser INSTANCE_DIRS = new MyFileChooser(false);

    private final JFileChooser dynamicChooser;

    private MyFileChooser(boolean filesOnly) {
        this.isFilesOnly = filesOnly;
        this.dynamicChooser = new JFileChooser();
        dynamicChooser.setControlButtonsAreShown(false);
        this.chooserSetup();
    }

    public MyFileChooser getInstance(boolean filesOnly) {
        if (filesOnly) {
            return MyFileChooser.INSTANCE_FILES;
        } else {
            return MyFileChooser.INSTANCE_DIRS;
        }
    }

    public void frameSetup(Container parent) {
        // jframe setup code
        parent.add(this.dynamicChooser);
    }

    public void chooserSetup() {
        if (this.isFilesOnly) {
            this.dynamicChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        } else {
            this.dynamicChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        }
        this.dynamicChooser.setMultiSelectionEnabled(true);
        this.dynamicChooser.setDialogType(JFileChooser.CUSTOM_DIALOG);
    }
    
    public File[] showOpenDialog(Component parent, String title) {
        JDialog dialog = new JDialog(parent == null ? null : SwingUtilities.getWindowAncestor(parent), title);
        dialog.setModal(true);
        frameSetup(dialog);
        
        dialog.pack();
        dialog.setLocationRelativeTo(parent);
        
        dialog.setVisible(true);
        
        return dynamicChooser.getSelectedFiles();
    }
}

目的是模仿JFileChoosershowDialog的动作。在此示例中,我隐藏了“普通”控制按钮,因为我假设您将提供自己的按钮,通过它您可以更改 return 文件

现在,如果您仍想使用标准按钮控件,可以将 ActionListener 附加到 JFileChooser

dynamicChooser.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        //...
    }
});

当我测试时,取消按钮 return CancelSelection 和选择按钮 return ApproveSelection

现在,因为我的示例使用动态对话框,您可能需要创建一个可以控制对话框的动态 ActionListener,因为您不想继续添加 ActionListenerFileChooser 不删除它们,比如...

public File[] showOpenDialog(Component parent, String title) {
    JDialog dialog = new JDialog(parent == null ? null : SwingUtilities.getWindowAncestor(parent), title);
    dialog.setModal(true);
    frameSetup(dialog);

    ActionListener listener = new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            String cmd = e.getActionCommand();
            //..
            dialog.dispose();
        }
    };
    dynamicChooser.addActionListener(listener);

    dialog.pack();
    dialog.setLocationRelativeTo(parent);

    dialog.setVisible(true);
    dynamicChooser.removeActionListener(listener);

    return dynamicChooser.getSelectedFiles();
}

举个例子

观察

因为我知道如果我不这样做,有人会踢我来遏制...

Java 中的 Sington 是棘手的事情,关于这个主题有无数的线程,但是,人们普遍认为(现在)最好的方法是使用 enum,它解决了所有问题旧

的同步问题

因此,您可以考虑使用类似...

public static enum MyFileChooser {

    INSTANCE_FILES(true),
    INSTANCE_DIRS(false);

    private final boolean isFilesOnly;

    private final JFileChooser dynamicChooser;

    private MyFileChooser(boolean filesOnly) {
        this.isFilesOnly = filesOnly;
        this.dynamicChooser = new JFileChooser();
        //dynamicChooser.setControlButtonsAreShown(false);
        this.chooserSetup();
    }

    public void frameSetup(Container parent) {
        // jframe setup code
        parent.add(this.dynamicChooser);
    }

    public void chooserSetup() {
        if (this.isFilesOnly) {
            this.dynamicChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        } else {
            this.dynamicChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        }
        this.dynamicChooser.setMultiSelectionEnabled(true);
        this.dynamicChooser.setDialogType(JFileChooser.CUSTOM_DIALOG);
    }

    public File[] showOpenDialog(Component parent, String title) {
        JDialog dialog = new JDialog(parent == null ? (JDialog)null : SwingUtilities.getWindowAncestor(parent), title);
        dialog.setModal(true);
        frameSetup(dialog);

        ActionListener listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String cmd = e.getActionCommand();
                //..
                dialog.dispose();
            }
        };
        dynamicChooser.addActionListener(listener);

        dialog.pack();
        dialog.setLocationRelativeTo(parent);

        dialog.setVisible(true);
        dynamicChooser.removeActionListener(listener);

        return dynamicChooser.getSelectedFiles();
    }
}

您可以简单地使用类似...

File[] files = MyFileChooser.INSTANCE_FILES.showOpenDialog(null, "Open");

“我已经提到我希望在选择器之间使用 JLabel,”

我原以为布置评论只是阅读数以千计可用示例中的任何一个的问题,显然我错了

因此,对 frameSetup 方法进行简单更新即可解决该问题...

public void frameSetup(Container parent) {
    // jframe setup code
    parent.setLayout(new BorderLayout());
    parent.add(new JLabel("I'm on top"), BorderLayout.NORTH);
    parent.add(this.dynamicChooser);
    parent.add(new JLabel("I'm on bottom"), BorderLayout.SOUTH);
}

查看 How to Use BorderLayout 了解更多详情

为确保其正常工作,我更新了 showOpenDialog 方法以使用

frameSetup(dialog.getContentPane());

而不是仅仅传递 JDialog 的实例,这确保我们正在影响正确的容器