Почему я не могу получить KeyEvent.VK_TAB, когда использую привязку клавиш для JPanel

Я буду печатать соответствующую информацию, если пользователи сосредоточатся на текущем окне и нажмут клавишу. Однако это работает для некоторых клавиш, таких как «a», но не для «tab». Вот простая демонстрация:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;


public class KeyBindingTest {
    public static void main(String[] args) {
        KeyBindingTest test = new KeyBindingTest();
        test.createUI();
    }

    public void createUI(){
        JFrame frame = new JFrame("KeyBinding Test");
        MainPanel mainPanel = new MainPanel();
        frame.add(mainPanel,BorderLayout.CENTER);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    @SuppressWarnings("serial")
    class MainPanel extends JPanel{
        public MainPanel(){
            setPreferredSize(new Dimension(200, 200));
            //========================key binding============================
            requestFocusInWindow();
            String aString = "aStr";
            getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), aString);
            getActionMap().put(aString, new AbstractAction() {          
                @Override
                public void actionPerformed(ActionEvent e) {
                    // TODO Auto-generated method stub
                    System.out.println("a is typed");
                }
            });     

            String tabString = "tabStr";
            getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), tabString);
            getActionMap().put(tabString, new AbstractAction() {            
                @Override
                public void actionPerformed(ActionEvent e) {
                    // TODO Auto-generated method stub
                    System.out.println("tab is typed");
                }
            });     
        }   
    }
}

Что я могу сделать, чтобы достичь этого? Заранее спасибо.


person Eugene    schedule 17.07.2014    source источник
comment
Посмотрите на эту тему (совет Роба Camick с setFocusTraversalKeys(...) работает).   -  person alex2410    schedule 17.07.2014
comment
+1 за действительно забавный вывод, никогда не сталкивался, понятия не имею, как ...   -  person mKorbel    schedule 17.07.2014


Ответы (3)


Цитата из Как использовать подсистему Focus (Учебные руководства по Java™ > Создание графического интерфейса С помощью JFC/Swing > Использование других функций Swing) (предложено @alex2410(ссылка на сообщение @camickr) и @mKorbel):

В большинстве моделей Look and Feel навигация по компонентам осуществляется с помощью клавиш Tab и Shift-Tab. Эти клавиши являются клавишами обхода фокуса по умолчанию и могут быть изменены программно.
...
Tab перемещает фокус в прямом направлении. Shift-Tab перемещает фокус в обратном направлении. Табуляция перемещает фокус через кнопки в текстовую область. Дополнительная табуляция перемещает курсор в пределах текстовой области, но не за ее пределы, поскольку внутри текстовой области клавиша Tab не является клавишей обхода фокуса. Однако Control-Tab перемещает фокус из текстовой области в первое текстовое поле. Точно так же Control-Shift-Tab перемещает фокус из текстовой области в предыдущий компонент.
...
Клавиша Control используется по соглашению для перемещения фокуса из любого компонента, который обрабатывает Tab специальным образом, например JTable.
Вы только что получили краткое введение в архитектуру фокуса. Если вам нужны дополнительные сведения, см. спецификацию Подсистема фокуса.

Поэтому, если вы хотите, чтобы действие Tab KeyBinding работало на панели, вам нужно удалить навигацию фокуса клавиши Tab с панели.

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

//http://stackoverflow.com/q/24800417/714968
public class KeyBindingTest3 {
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame("KeyBinding Test");
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new MainPanel());
    f.setSize(320, 240);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}

class MainPanel extends JPanel {
  public MainPanel() {
    super();
    //@see JTable constructor
    Set<KeyStroke> forwardKeys = new HashSet<KeyStroke>(1);
    forwardKeys.add(KeyStroke.getKeyStroke(
        KeyEvent.VK_TAB, InputEvent.CTRL_MASK));
    setFocusTraversalKeys(
        KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys);

    Set<KeyStroke> backwardKeys = new HashSet<KeyStroke>(1);
    backwardKeys.add(KeyStroke.getKeyStroke(
        KeyEvent.VK_TAB, InputEvent.SHIFT_MASK | InputEvent.CTRL_MASK));
    setFocusTraversalKeys(
        KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, backwardKeys);

    setPreferredSize(new Dimension(200, 200));

    String aString = "aStr";
    getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), aString);
    getActionMap().put(aString, new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        System.out.println("a is typed");
      }
    });
    String tabString = "TAB";
    getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
      KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), tabString);
    getActionMap().put(tabString, new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        System.out.println("tab is typed");
      }
    });
  }
}
person aterai    schedule 17.07.2014

  • интересная проблема с TAB, выглядит как ошибка, потому что невозможно получить, захватить KeyChar из TAB без использования Shift_TAB до этого, событие из TAB каким-то образом потребляется в другом месте, понятия не имею, что произошло

  • мое мнение - есть проблема с Focus, потому что клавиша TAB используется Native OS и как встроенная KeyBindings в Swing,

  • противоположная проблема с TAB и Shift_TAB в вопросе Java Swing: как чтобы остановить нежелательное нажатие клавиши Shift-Tab

  • может быть, у кого-то есть объяснение, как поймать событие TAB

  • TAB используется как KeyBindings (встроенный в API) для многих JComponents или навигации внутри Container содержит более одного JComponent

забавный вывод из AWTEventListener (win8_64b/Java7_xxx)

     is typed //tab is pressed
     is typed
     is typed
     is typed
     is typed
     is typed
     is typed
     is typed
     is typed
 is typed //shift is pressed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
 is typed
     is typed // tab is pressed again
     is typed
     is typed
     is typed
 is typed //:-) nobody knows something from milky way
 is typed
     is typed
shift tab is typed //now is tab event unlocked for Container in Swing
shift tab is typed
shift tab is typed
ctrl tab is typed
ctrl tab is typed
ctrl tab is typed
tab is typed // now is possible, finally TAB is unlocked and firing an event
tab is typed
tab is typed

из кода

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

//https://stackoverflow.com/q/24800417/714968
public class KeyBindingTest {

    public static void main(String[] args) {
        KeyBindingTest test = new KeyBindingTest();
        test.createUI();
    }

    public void createUI() {
        JFrame frame = new JFrame("KeyBinding Test");
        MainPanel mainPanel = new MainPanel();
        frame.add(mainPanel, BorderLayout.CENTER);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
            @Override
            public void eventDispatched(AWTEvent event) {
                if (event instanceof KeyEvent) {
                    KeyEvent ke = (KeyEvent) event;
                    System.out.println(ke.getKeyChar() + " is typed");
                }
            }
        }, AWTEvent.KEY_EVENT_MASK);
    }

    @SuppressWarnings("serial")
    class MainPanel extends JPanel {

        public MainPanel() {
            setPreferredSize(new Dimension(200, 200));
            //========================key binding============================
            //requestFocusInWindow();
            String aString = "aStr";

            getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), aString);
            getActionMap().put(aString, new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // TODO Auto-generated method stub
                    System.out.println("a is typed");
                }
            });
            String tabString = "TAB";
            this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(tabString), tabString);
            //getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), tabString);
            this.getActionMap().put(tabString, new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // TODO Auto-generated method stub
                    System.out.println("tab is typed");
                }
            });
            String tabShiftString = "shift TAB";
            this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(tabShiftString), tabShiftString);
            //getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), tabString);
            this.getActionMap().put(tabShiftString, new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // TODO Auto-generated method stub
                    System.out.println("shift tab is typed");
                }
            });
            String ctrlShiftString = "ctrl TAB";
            this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(ctrlShiftString), ctrlShiftString);
            //getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), tabString);
            this.getActionMap().put(ctrlShiftString, new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    // TODO Auto-generated method stub
                    System.out.println("ctrl tab is typed");
                }
            });
        }
    }
} 
person mKorbel    schedule 17.07.2014
comment
Спасибо за ваши усилия, и вы так серьезно. - person Eugene; 18.07.2014

Я думаю, что это должно работать, если вы поставите

setFocusTraversalKeysEnabled(false);

в вашем конструкторе MainPanel. По крайней мере, это работает, например. addKeyListener(...);

person Jonas Berlin    schedule 28.01.2016