8

JFileChooser我在我自己的框架中的程序中嵌入了一个框架中的其他自定义组件。这是我的应用程序的设计,因为它可能有助于可视化我的问题:

我如何使用 JFileChooser

如果你看不出来,直接在JFrame标题下的列表是JFileChoosers. 这应该工作的方式是您将快捷方式分配给目的地,然后当您按下这些快捷键时,所选文件将移动到目的地。

我这样做的策略是将快捷方式分配给整个框架 的javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW范围。InputMap

但令人讨厌的是,某些东西(我认为JFileChooser)不断响应/吸收我不希望的按键。例如,如果我按下Ctrl+C我的快捷操作不会运行。我已经用原生的外观和感觉(我使用的是 Windows 7)和默认的 L&F 进行了尝试,这两种情况都有同样的问题。我认为它可能正在尝试对所选文件执行复制操作,JFileChooser因为如果我单击其中一个按钮以强制它失去焦点,我的Ctrl+C命令会突然执行我的操作。

但是,我不确定这JFileChooser是如何做到的。当我调用getKeyListeners()它时,它返回一个空数组。我还尝试在所有三个范围内清除此组合键的输入映射,它似乎仍在吸收按键。

谁能给我一些使JFileChooser忽略的示例代码Ctrl+C?另外,如果有人能告诉我将来如何调试这样的问题,那将会很有帮助。


这是我迄今为止尝试过的一些代码。您也可以使用它来尝试自己测试它,因为此代码按原样编译和运行:

package com.sandbox;

import javax.swing.*;
import java.awt.event.ActionEvent;

public class Sandbox {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("control C"), "println");
        panel.getActionMap().put("println", new AbstractAction() {
            public void actionPerformed(ActionEvent e) {
                System.out.println("The JPanel action was performed!");
            }
        });

        panel.add(buildFileChooser());  //if you comment out this line, Ctrl+C does a println, otherwise my action is ignored.

        frame.setContentPane(panel);

        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    private static JFileChooser buildFileChooser() {
        JFileChooser fileChooser = new JFileChooser();        
        fileChooser.getActionMap().clear(); //I've tried lots of ideas like this, but the JFileChooser still responds to Ctrl+C
        return fileChooser;
    }
}

更新:我已经递归地清除 inputMaps 并删除 JFileChooser 的 keyListeners 及其所有子组件,而 JFileChooser仍然吞下我的 Ctrl+C 命令。这是我用来执行此操作的代码(我将 JFileChooser 传递给此代码):

private static void removeKeyboardReactors(JComponent root) {
    System.out.println("I'm going to clear the inputMap of: " + root);
    root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).clear();
    root.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).clear();
    root.getInputMap(JComponent.WHEN_FOCUSED).clear();
    root.getActionMap().clear();

    if (root.getRootPane() != null) {
        removeKeyboardReactors(root.getRootPane());
    }

    for (KeyListener keyListener : root.getKeyListeners()) {
        root.removeKeyListener(keyListener);
    }

    for (Component component : root.getComponents()) {
        if (component instanceof JComponent) {
            removeKeyboardReactors((JComponent) component);
        } else if (component instanceof Container) {
            Container container = (Container) component;
            for (Component containerComponent : container.getComponents()) {
                if (containerComponent instanceof JComponent) {
                    removeKeyboardReactors((JComponent) containerComponent);
                } else {
                    System.out.println("This Container Component was not a JComponent: " + containerComponent);
                }
            }
        } else {
            System.out.println("This was not a JComponent: " + component);
        }
    }
}
4

2 回答 2

4

详细信息视图仍将有一个填充的输入图

我怀疑详细视图和列表视图之间的区别在于一个使用 JTable 而另一个使用 JList。所以我猜你只需要从细节视图的 JTable 中删除绑定。

这可以在不创建详细信息面板的情况下完成:

InputMap im = (InputMap)UIManager.get("Table.ancestorInputMap");
KeyStroke ctrlC = KeyStroke.getKeyStroke("control C");
//im.put(ctrlC, "none");
im.remove(ctrlC);

同样,应该注意的是,此解决方案(以及您当前拥有的解决方案)将删除所有组件的默认 Ctrl+C 功能,而不仅仅是为 JFileChooser 实例化的那些。

编辑:

它不应该只从我删除它的那些中删除它吗?

您的代码使用 getParent() 方法来获取包含绑定的 InputMap。此 InputMap 由组件的所有实例共享。一个组件只有在你使用时才会有唯一的绑定:

component.getInputMap(...).put(...);

也就是说,绑定被添加到组件 InputMap,而不是其父 InputMap。

你怎么知道你能做到这一点,这是正确的做法

请参阅UIManager 默认值。这列出了给定 LAF 的默认值。我不知道这是否是正确的做法。据我所知,效果与您现在使用的代码相同。这只是另一种方式,或者从 InputMap 中删除绑定,而不需要实际的组件来访问父 InputMap。

第二次编辑:

显示 InputMap 的一些简单代码是相同的:

public static void main(String[] args)
{
    JButton first = new JButton("button");
    System.out.println(first.getInputMap().getParent());

    InputMap im = (InputMap) UIManager.get("Button.focusInputMap");
    System.out.println(im);
}
于 2013-04-30T16:20:32.340 回答
2

显然 InputMaps 可以有父母。因此,您对所有内置关键“反应器”的清除并未完全完成。您可能已经猜到了,Swing 在某些组件上为自己注册了某些默认键盘绑定。在 Windows 上,这通常包括Ctrl+ C,因为这是用于将数据复制到剪贴板的操作系统标准热键。

修改后removeKeyboardReactors的 System.out.println 出现在我面前:

private static void removeKeyboardReactors(JComponent root) {
    System.out.println("I'm going to clear the inputMap of: " + root);
    clearInputMap(root.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
    clearInputMap(root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW));
    clearInputMap(root.getInputMap(JComponent.WHEN_FOCUSED));

    for (KeyListener keyListener : root.getKeyListeners()) {
        root.removeKeyListener(keyListener);
    }

    for (Component component : root.getComponents()) {
        if (component instanceof JComponent) {
            removeKeyboardReactors((JComponent) component);
        } else if (component instanceof Container) {
            Container container = (Container) component;
            for (Component containerComponent : container.getComponents()) {
                if (containerComponent instanceof JComponent) {
                    removeKeyboardReactors((JComponent) containerComponent);
                } else {
                    System.out.println("This Container Component was not a JComponent: " + containerComponent);
                }
            }
        } else {
            System.out.println("This was not a JComponent: " + component);
        }
    }
}

private static void clearInputMap(InputMap inputMap) {
    inputMap.clear();
    while ((inputMap = inputMap.getParent()) != null) {
        inputMap.clear();
    }
}

我不得不删除这段代码,removeKeyboardReactors因为它导致堆栈溢出:

if (root.getRootPane() != null) {
    removeKeyboardReactors(root.getRootPane());
}

整个修改后的 Sandbox 类如下所示。希望这足以让您上路。如果您希望键绑定删除更加特定于键,请查看InputMap#remove(KeyStroke)

public class Sandbox
{

    public static void main(String[] args)
    {
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("control C"), "println");
        panel.getActionMap().put("println", new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                System.out.println("The JPanel action was performed!");
            }
        });

        JFileChooser fileChooser = buildFileChooser();
        panel.add(fileChooser); //if you comment out this line, Ctrl+C does a println, otherwise my action is ignored.

        frame.setContentPane(panel);

        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();

        removeKeyboardReactors(fileChooser);

        frame.setVisible(true);
    }

    private static JFileChooser buildFileChooser()
    {
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.getActionMap().clear(); //I've tried lots of ideas like this, but the JFileChooser still responds to Ctrl+C
        return fileChooser;
    }

    private static void clearInputMap(InputMap inputMap)
    {
        inputMap.clear();
        while ((inputMap = inputMap.getParent()) != null)
        {
            inputMap.clear();
        }
    }

    private static void removeKeyboardReactors(JComponent root) {
        System.out.println("I'm going to clear the inputMap of: " + root);
        clearInputMap(root.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
        clearInputMap(root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW));
        clearInputMap(root.getInputMap(JComponent.WHEN_FOCUSED));

        for (KeyListener keyListener : root.getKeyListeners()) {
            root.removeKeyListener(keyListener);
        }

        for (Component component : root.getComponents()) {
            if (component instanceof JComponent) {
                removeKeyboardReactors((JComponent) component);
            } else if (component instanceof Container) {
                Container container = (Container) component;
                for (Component containerComponent : container.getComponents()) {
                    if (containerComponent instanceof JComponent) {
                        removeKeyboardReactors((JComponent) containerComponent);
                    } else {
                        System.out.println("This Container Component was not a JComponent: " + containerComponent);
                    }
                }
            } else {
                System.out.println("This was not a JComponent: " + component);
            }
        }
    }
}
于 2013-04-29T18:40:14.157 回答