8

JFileChooserフレーム内の他のカスタムコンポーネントを使用して、自分のフレーム内のプログラムに を埋め込んでいます。問題を視覚化するのに役立つかもしれない私のアプリのデザインは次のとおりです。

JFileChooser の使用方法

わからない場合は、JFrameタイトルのすぐ下のリストがJFileChoosers. これが機能するはずの方法は、ショートカットを宛先に割り当て、それらのショートカットキーを押すと、選択したファイルが宛先に移動することです。

これを行うための私の戦略は、ショートカットをフレーム全体の javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOWスコープに割り当てることです。InputMap

しかし、厄介なのは、何か (私は と仮定しますJFileChooser) が、私が望んでいないキーの押下に応答/吸収し続けることです。たとえばCtrl+C、ショートカット アクションを押しても実行されません。ネイティブのルック アンド フィール (Windows 7 を使用) とデフォルトの L&F でこれを試しましたが、どちらの状況でも同じ問題が発生します。JFileChooserボタンの 1 つをクリックして強制的にフォーカスを失うと、突然Ctrl+Cコマンドがアクションを実行するため、選択したファイルのコピー アクションを実行しようとしている可能性があると思います

しかし、私はこれをどのようにJFileChooser行っているのかよくわかりません。それを呼び出すgetKeyListeners()と、空の配列が返されます。また、3 つのスコープすべてでこのキーの組み合わせの入力マップをクリアしようとしましたが、まだキープレスを吸収しているようです。

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

UPDATE : 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);

繰り返しますが、このソリューション (現在のソリューションと同様) は、JFileChooser 用にインスタンス化されたものだけでなく、すべてのコンポーネントのデフォルトの Ctrl+C 機能を削除することに注意してください。

編集:

削除したものから削除するだけではいけませんか?

コードは getParent() メソッドを使用して、バインディングを含む InputMap を取得します。この InputMap は、コンポーネントのすべてのインスタンスによって共有されます。以下を使用する場合、コンポーネントは一意のバインディングのみを持ちます。

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

つまり、バインディングは、親の InputMap ではなく、コンポーネントの InputMap に追加されます。

これができること、これが正しいことだとどうしてわかったのですか?

UIManager のデフォルトを参照してください。これは、指定された LAF のデフォルトをリストします。これが正しいことかどうかはわかりません。私の知る限り、効果は現在使用しているコードと同じです。これは、親 InputMap にアクセスするための実際のコンポーネントを必要とせずに、InputMap からバインディングを削除する別の方法です。

2番目の編集:

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は、データをクリップボードにコピーするための OS 標準のホットキーであるためです。

これを変更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 に答える