Java: как удалить KeyStrokes по умолчанию из любого JComponent?

Я хочу контролировать, какое нажатие клавиши принадлежит какому Jcomponent. Я даже хочу узнать, как удалить нажатия клавиш по умолчанию, связанные с Jcomponent, и заменить их другими любимыми нажатиями клавиш.

Я следовал этому оракулу учебнику, он дает пример с JButton я попробовал, и он работает нормально, но когда я попробовал это с JComboBox не работает!

Что именно я пробовал, так это удалить клавишу SPACE, то есть предотвратить ответ JComponent на нажатия SPACE

Я использовал этот код для удаления клавиши ПРОБЕЛ:

firstButton.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "none");

То же самое для JComboBox

sizesComboBox.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "none");

Но не работает, что он (JComboBox) все равно реагирует на клавишу ПРОБЕЛ

Для firstButton, что я убрал эффект нажатия ПРОБЕЛ; Я добавил клавишу F, чтобы теперь при нажатии клавиши F на клавиатуре нажималась firstButton, и не реагировал на ПРОБЕЛ ( предназначена). Обратите внимание, что нажатие F имеет место, даже если firstButton не имеет фокуса (JComponent.WHEN_IN_FOCUSED_WINDOW)

Это код SSCCE, показывающий мой пример:
Примечание: я намеренно не добавлял приведенную выше строку кода ко второй кнопке "secondButton", поэтому она по-прежнему реагирует на ПРОБЕЛ< /kbd> по умолчанию.

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.*;

public class KeyStrokeTest extends JPanel
{

    JPanel widgetPanel;
    JPanel textAreaPanel;
    JButton firstButton;
    JButton secondButton;
    JTextArea textArea;
    JComboBox<Integer> sizesComboBox;

    public KeyStrokeTest()
    {
        firstButton = new JButton("First");
        firstButton.addActionListener(eventWatcher);
        firstButton.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "none");
        firstButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F"), "F Key");
        firstButton.getActionMap().put("F Key", eventWatcher);

        secondButton = new JButton("Second");
        secondButton.addActionListener(eventWatcher);

        sizesComboBox = new JComboBox<>();
        sizesComboBox.addItemListener(new itemListenerClass());
        for (int i = 1; i <= 8; i++)
        {
            sizesComboBox.addItem(i);
        }
        sizesComboBox.setSelectedIndex(0);
        sizesComboBox.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "none");


        textArea = new JTextArea(0, 0);
        JScrollPane scrollTextArea = new JScrollPane(textArea);
        scrollTextArea.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        textArea.setEditable(false);

        widgetPanel = new JPanel();
        textAreaPanel = new JPanel(new BorderLayout());

        widgetPanel.add(firstButton);
        widgetPanel.add(secondButton);
        widgetPanel.add(sizesComboBox);

        textAreaPanel.add(scrollTextArea, BorderLayout.CENTER);

        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, textAreaPanel, widgetPanel);
        splitPane.setDividerLocation(280);
        splitPane.setResizeWeight(.5d);
        this.setLayout(new BorderLayout());
        this.add(splitPane);
    }
    AbstractAction eventWatcher = new AbstractAction()
    {
        @Override
        public void actionPerformed(ActionEvent ae)
        {
            Object source = ae.getSource();
            if (source == firstButton)
            {
                textArea.append("First button clicked\n");
            }
            if (source == secondButton)
            {
                textArea.append("Second button clicked\n");
            }
        }
    };

    private class itemListenerClass implements ItemListener
    {

        @Override
        public void itemStateChanged(ItemEvent e)
        {
            if (e.getSource() == sizesComboBox)
            {
                if (textArea != null)
                {
                    textArea.append("Item " + sizesComboBox.getSelectedItem() + "\n");
                }
            }
        }
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("KeyStroke Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(500, 300);
        frame.add(new KeyStrokeTest(), BorderLayout.CENTER);
        frame.setVisible(true);
    }

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                UIManager.put("swing.boldMetal", Boolean.FALSE);
                createAndShowGUI();
            }
        });
    }
}

Причина, по которой я хочу управлять нажатиями клавиш по умолчанию для JComponent, заключается в том, что я хочу удалить эффект по умолчанию ПРОБЕЛ для всех JComponent, кроме одной кнопки, которая будет реагировать на ПРОБЕЛ нажимает везде, где находится фокус, используя JComponent.WHEN_IN_FOCUSED_WINDOW, так что щелчок по другому компоненту (и перемещение фокуса с исключенной кнопки) не предотвратит действие ПРОБЕЛ на эта кнопка.


Еще один момент: если вы протестировали приведенный выше код, вы заметите, что выбор элемента из JComboBox создает две строки, если вы выбираете элемент «4», вывод в JTextArea

Item 4
Item 4

Почему два??

Спасибо.


person Saleh Feek    schedule 27.01.2013    source источник
comment
Присвоение клавише нового имени многие не отменяют те привязки, которые уже существуют (или уже зарегистрированы). Возможно, вы захотите взглянуть на это для некоторых подсказок, но вам нужно будет знать имя привязки клавиш, используемой внешним видом, чтобы заставить его работать   -  person MadProgrammer    schedule 27.01.2013
comment
to Another point: ItemListener запускает всегда два события, необходимо определить, SELECTED/DESELECTED   -  person mKorbel    schedule 27.01.2013


Ответы (1)


Но не работает, что он (JComboBox) все еще реагирует на клавишу ПРОБЕЛ

Вы должны использовать JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT InputMap так (как вы могли заметить, я использую KeyEvent и KeyStroke.getKeyStroke(int key,int modifier,boolean onRelease) как более читаемый и менее подверженный ошибкам, т.е. ввод неправильного строкового аргумента и т. д.):

sizesComboBox.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
        .put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,0,false), "none");

Насколько я понимаю, причина этого прекрасно объяснена здесь:

Компонент содержит (или является) компонентом, который имеет фокус. Эта входная карта обычно используется для составного компонента — компонента, реализация которого зависит от дочерних компонентов. Например, JTable делают все свои привязки, используя WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, так что, если пользователь редактирует, клавиша стрелка вверх (например) по-прежнему изменяет выбранную ячейку.

Итак, я делаю вывод, что JCombobox является составным компонентом, и поэтому нам нужны правильные InputMap - WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, чтобы удалить все его внутренние компоненты KeyBinding для определенной клавиши, т.е. SPACE.

Еще один момент: если вы протестировали приведенный выше код, вы заметите, что выбор элемента из JComboBox создает две строки, если вы выбираете элемент «4», вывод в JTextArea

Item 4
Item 4

Почему два??

Как сказал @mKorbel (+1 к его комментарию), могут произойти 2 события:

  • элемент не выбран
  • элемент выбран

Эти события происходят парами, так как когда мы выбираем новое значение, старое отменяется. Таким образом, мы должны проверить это и действовать соответствующим образом:

@Override
public void itemStateChanged(ItemEvent e)
{
   if(e.getStateChange()==ItemEvent.SELECTED) { 
       //am item was selected do something
   }
}

Другие предложения:

  • Не звоните setSize JFrame.

  • Используйте соответствующий LayoutManager и/или переопределите getPreferredSize, чтобы вернуть Dimension, которые соответствуют содержимому, и вызовите pack() для JFrame до того, как настройка станет видимой, и после добавления компонентов.

person David Kroukamp    schedule 27.01.2013