【问题标题】:How to attach handler for JFileChooser buttons inside custom JFrame in Java如何在 Java 中的自定义 JFrame 中附加 JFileChooser 按钮的处理程序
【发布时间】:2017-02-04 00:32:41
【问题描述】:

我有一个名为 MyFileChooser 的单例模式类,它根据构造函数参数适应 JFileChooser 是 FILES_ONLY 还是 DIRECTORIES_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 的“关闭”和“打开”按钮附加处理程序。我唯一发现的是:

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

但是,如果调用此方法并且即使没有来自所需结构的 JFrame wrap 和 JLabels,它也会打开两个实例。 所以我的问题是如何从 JFileChooser 中获取“打开”和“关闭”按钮以附加处理程序(不同的实例)或如何通过其他方式处理选定的文件。

【问题讨论】:

    标签: java swing singleton jfilechooser


    【解决方案1】:

    你的第一个问题来了

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

    它调用底层JFileChoosershowDialog方法,所以它正在构建自己的窗口,忽略你的

    更好的解决方案是按需创建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();
        }
    }
    

    其目的是模仿JFileChooser 中的showDialog 的动作。在这个例子中,我隐藏了“普通”控制按钮,因为我假设你会提供你自己的,通过它你可以更改返回文件

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

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

    我在测试时,取消按钮返回CancelSelection,选择按钮返回ApproveSelection

    现在,因为我的示例使用动态对话框,您可能需要创建一个动态的ActionListener 来控制对话框,因为您不想在不删除它们的情况下继续将ActionListeners 添加到FileChooser ,类似...

    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's 是棘手的事情,关于这个主题有无数的线程,但是,通常认为(现在)最好的方法是使用 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");
    

    “我已经提到我希望在选择器之间有 JLabels,”

    我曾认为布置 cmets 只是阅读数千个可用示例中的任何一个,显然我错了

    所以,对 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 的实例,而是确保我们正在影响正确的容器

    【讨论】:

    • 感谢您的回答。我忘了提到我希望使用默认按钮(简单的打开获取文件,关闭隐藏框架)。但是,您已经更新了 :) 不幸的是,我无法对此进行测试,因为当我尝试编译该类时,它会抛出错误“对 JDialog 的引用不明确”。
    • parent == null ? null : ... 更改为 parent == null ? (JDialog)null : ...
    • 以上解决了模棱两可的问题。老实说,我认为枚举解决方案非常好,但是,它没有在我的项目中显示 JFrame 和请求的 JLabels,只是 JFileChooser 窗口。
    • 那是因为您的示例代码没有包含任何其他组件。尝试将BorderLayout 应用于传递给frameSetupcontainer,然后将组件添加到NORTHSOUTH 位置
    • 我已经提到我希望在选择器之间有 JLabels,它们在 frameSetup 方法中初始化。我只是“修复”它,但 JFileChooser 不在 JFrame 内,但它们是分开的,知道我在哪里做错了吗? (也关闭选择器不要关闭 JFrame)
    猜你喜欢
    • 1970-01-01
    • 2014-08-01
    • 1970-01-01
    • 2010-09-23
    • 2012-05-23
    • 2015-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多