Swing - 如何立即获取焦点?

3

如何立即让我的Swing组件获得焦点?requestFocus()似乎不能立即调用。

理想情况下,我希望从EDT运行以下代码:

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

打印true

以下是SSCCE。注/要求:

  1. 使用键盘导航表格。列C2具有复合编辑器。
  2. 当我在C2列输入字母时,开始编辑。复合编辑器内的文本组件获得焦点。需要在标有“Trick”的注释中实现此点。
  3. 文本字段是一个具有焦点侦听器干扰我的代码的第三方编辑器。这里模拟为selectAll()

目前分派的顺序是:在文本组件中输入字母,然后分派焦点侦听器。因为文本字段具有焦点,所以下一个字母被正确分派了。

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


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

你遇到了什么实际问题?请发布你的SSCCE(http://sscce.org)以展示问题,或许有人可以提供解决方案。我猜你可以使用FocusListener,但是没有更多细节,我不能确定。 - camickr
@camickr 我已经更新了问题并提供了SSCCE。 - Konrad Garus
3个回答

2

我明白了。

  1. AncestorListenerprocessKeyBinding()中的事件都是处理同一个事件:"键入"。
  2. 显然,抓取焦点的唯一方法是requestFocus(),它会在由“键入”触发的当前事件流之后添加到事件队列中。因此,抓取焦点和执行FocusListener将始终稍后执行。

解决方案是:在processKeyBinding()中,不要立即将按键传递给内部组件。将其排队在事件队列中,以便在焦点转移和监听器之后执行。也就是说,包装:

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

使用SwingUtilities.invokeLater()方法。


0

我不知道如何控制事件的顺序,通常来说这不是你应该尝试做的事情。

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

也许你可以从编辑器中删除FocusListener。也许你可以通过使用...直接调用监听器。

savedFocusListener.focusGained(FocusEvent);

在 AncestorListener 代码中设置焦点于文本字段。

0

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


这并不是非常信息丰富的。对于用户输入而言,状态变化通常会在事件开始派发后同步进行(例如,当鼠标按下时,JButton状态会改变)。在AWT中,焦点更改必须返回到事件队列中是一种异常情况。 - yonran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接