摇摆 - 如何抓住焦点*现在*?

时间:2010-08-02 15:43:33

标签: java swing

如何指示我的Swing组件立即获取焦点 requestFocus()似乎没有立即发送。

理想情况下,我希望这样(从EDT开始):

textInput.requestFocusInWindow();
System.out.println(textInput.hasFocus());

打印true

以下是SSCCE。备注/要求:

  1. 使用键盘导航表格。 C2列有一个复合编辑器。
  2. 当我在C2栏中输入字母时,编辑开始。复合编辑器中的文本组件获得焦点。它需要输入启动编辑器的字母。这一点的实施标有“Trick”的评论。
  3. 文本字段是第三方编辑器,其焦点侦听器干扰我的代码。这里将其模拟为selectAll()
  4. 目前发送的顺序是:在文本组件中键入字母,然后调度焦点监听器。然后正确调度下一个字母,因为它是具有焦点的文本字段。

    我需要它将焦点设置在文本组件上,调度焦点侦听器,然后将键事件传递给它。


    public class JTableIssue extends JFrame {
        public JTableIssue() {
            JTable table = new JTable(new Object[][] {
                    { "Apple", "Orange", "Strawberry" },
                    { "Pineapple", "Orange", "Zergz" } }, new Object[] { "C1",
                    "C2", "C3" });
            table.getColumn("C2").setCellEditor(new MyEditor());
            add(new JScrollPane(table));
            pack();
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
    
        public static void main(String[] args) {
            new JTableIssue().setVisible(true);
        }
    }
    
    class MyEditor extends AbstractCellEditor implements TableCellEditor {
        MyTextField textField = new MyTextField();
        JPanel panel;
    
        MyEditor() {
            panel = new JPanel(new BorderLayout()){
                // Trick: Pass all key typed to text field
                @Override
                protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
                        int condition, boolean pressed) {
                    if (ks.getKeyEventType() == KeyEvent.KEY_TYPED) {
                        textField.processKeyBinding(ks, e, condition, pressed);
                    }
                    return super.processKeyBinding(ks, e, condition, pressed);
                }
            };
            textField.addFocusListener(new FocusAdapter() {
                @Override
                public void focusGained(FocusEvent e) {
                    textField.selectAll();
                }
            });
            panel.add(textField, BorderLayout.CENTER);
            // Trick: Pass focus to text field when editor is added to table
            panel.addAncestorListener(new AncestorListener() {
                public void ancestorRemoved(AncestorEvent event) {
                }
    
                public void ancestorMoved(AncestorEvent event) {
                }
    
                public void ancestorAdded(AncestorEvent event) {
                    textField.requestFocus();
                }
            });
        }
    
        public Object getCellEditorValue() {
            return textField.getText();
        }
    
        public Component getTableCellEditorComponent(JTable table, Object value,
                boolean isSelected, int row, int column) {
            textField.setText(value.toString());
            return panel;
        }
    }
    
    class MyTextField extends JTextField {
        // Trick: "public"
        @Override
        public boolean processKeyBinding(javax.swing.KeyStroke ks,
                java.awt.event.KeyEvent e, int condition, boolean pressed) {
            return super.processKeyBinding(ks, e, condition, pressed);
        };
    }
    

3 个答案:

答案 0 :(得分:2)

我明白了。

  1. AncestorListenerprocessKeyBinding()中的事件是处理同一事件的一部分:“键入类型”。
  2. 显然,获取焦点的唯一方法是requestFocus(),它会在“key typed”触发的当前事件流之后添加到事件队列中。因此,抓住焦点并执行FocusListener将始终执行。
  3. 解决方案是:在processKeyBinding()中,不要立即将密钥传递给内部组件。将其排入事件队列,以便在焦点传输和侦听器之后执行。也就是说,换行:

    if (ks.getKeyEventType() == KeyEvent.KEY_TYPED) {
        textField.processKeyBinding(ks, e, condition, pressed);
    }
    

    进入SwingUtilities.invokeLater()

答案 1 :(得分:0)

我不认为这是可能的。 UI操作本质上是异步的(尽管通常非常快),并且无法强制它们同步运行。如果你确实需要这个,可以在文本输入的焦点处理程序中触发事件,并在另一个线程中等待该事件。

答案 2 :(得分:0)

我不知道如何控制事件的顺序,一般而言,这不是你应该尝试做的事情。

  

3.文本字段是第三方编辑器,具有焦点监听器干扰   用我的代码

也许您可以从编辑器中删除FocusListener。也许你可以使用

直接调用监听器
savedFocusListener.focusGained(FocusEvent);

在AncestorListener代码中,将焦点设置在文本字段上。