【问题标题】:Java Swing: Focus issueJava Swing:焦点问题
【发布时间】:2011-08-29 09:58:45
【问题描述】:

我正在为我的游戏制作关卡编辑器。我有一个属性面板,我可以在其中修改所选对象的属性。我还有一个 Save 按钮来编写关卡 xml。

当编辑器组件失去焦点Enter被按下时,一个字段编辑被提交(*)。这很好用,但唯一的问题是当我有这个动作序列时:

  1. 编辑字段
  2. 按保存按钮

因为,发生的事情是这样的:

  1. 我编辑了字段
  2. 我按下保存按钮
  3. 关卡已保存
  4. 字段失去焦点
  5. 修改已提交

如您所见,这是错误的顺序。当然,我希望该字段失去焦点,这会导致提交并然后保存关卡。

是否有技巧、hack 或解决方法使该字段首先失去焦点,然后执行保存按钮的动作侦听器?

提前致谢。

(* submit = 对字段的编辑也在对象属性中进行)


编辑:对于我使用带有focusLost 的FocusAdapter 的字段:

FocusAdapter focusAdapter = new FocusAdapter()
{

    @Override
    public void focusLost(FocusEvent e)
    {
        compProperties.setProperty(i, getColor());
        record(); // For undo-redo mechanism
    }
};

对于按钮,一个简单的 ActionListener 带有 actionPerformed`。

btnSave.addActionListener(new java.awt.event.ActionListener() {
     public void actionPerformed(java.awt.event.ActionEvent evt) {
         // Save the level
     }
});

【问题讨论】:

  • 不知道您的代码是如何工作的,请在此处发布相关代码,因为还有其他选项可以使用DocumentListener,或者使用AncesorListener,或者只是将FocucHell 包装成invokeLatermyTextField.setText(myTextField.getText);
  • @mKorbel:我尝试将保存过程包装到invokeLater,但它的顺序仍然错误。
  • 另见Q&A
  • ActionListenerFocusListenerendless cycle 不存在并发,如果禁用Focus 会发生什么情况1) 为ActionFocus 创建单独的空白,2)使用Boolean 进行测试,是否从Focus 开始,或者从JButton 开始Action,3) 通过尝试错误设置eventsFocusAction 的正确顺序,其中一个必须从firing events 开始并首先结束,不知道compProperties.setProperty(i, getColor()); 和d record(); 是什么以及与// Save the level 的连接,我确定有你的A-Bomb

标签: java swing focus


【解决方案1】:

嗯...无法重现:在sn-p下面的丢失总是在actionPerfomed之前通知,与我是单击按钮还是使用助记符无关:

    final JTextField field = new JTextField("some text to change");
    FocusAdapter focus = new FocusAdapter() {

        @Override
        public void focusLost(FocusEvent e) {
            LOG.info("lost: " + field.getText());
        }

    };
    field.addFocusListener(focus);

    Action save = new AbstractAction("save") {

        @Override
        public void actionPerformed(ActionEvent e) {
            LOG.info("save: " + field.getText());
        }
    };
    save.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
    JButton button = new JButton(save);
    JComponent box = Box.createHorizontalBox();
    box.add(field);
    box.add(button);

另一方面,焦点是一个很难依赖的属性,排序可能取决于系统(我的是 win vista)。检查 sn-p 在您的设备上的行为。

  • 如果您看到与我相同的序列,则问题出在其他地方
  • 如果您在丢失之前获得了保存,请尝试将保存操作包装到 invokeLater 中(将其放在 EventQueue 的末尾,因此在所有未决事件之后执行)

    Action save = new AbstractAction("save") {
    
        @Override
        public void actionPerformed(ActionEvent e) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    LOG.info("save: " + field.getText());
                }
            });
        }
    };
    

【讨论】:

  • the problem is somewhere else 通过 OP 我添加了 I/O 流 1) 删除 javax.swing.Action 中的 Focus 和 ActionListener 2) 将 I/O 流重定向到 Runnable#thread, 3) 全部完成然后添加Focus & ActionListener back, 4) 如果我在两个步骤中将其延迟到两个 invokeLater(), 5) 使用 javax.swing.Timer & javax.swing.Action 从 ActionListener 处理事件, 6) 看起来像Action 与 ActionListener 略有不同 7) 所有关于将焦点从 JButton 移动到 JTexfield 的内容都包含在 invokeLater() +1
【解决方案2】:

通常,将您的保存代码包装到 SwingUtilities.invokeLater() 中应该可以解决问题。正如您已经提到的,这不起作用?试试这个:

private boolean editFocus = false;
FocusAdapter focusAdapter = new FocusAdapter()
{
   @Override
   public void focusGained(FocusEvent e){
        editFocus = true;
   }
   @Override
   public void focusLost(FocusEvent e){
       compProperties.setProperty(i, getColor());
       record(); // For undo-redo mechanism
       editFocus = false;
       if (saveRequested){
           save();               
       }
   }
};

对于您的按钮:

private boolean saveRequested = false;

btnSave.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
         if (editFocus){
             saveRequested = true;
             return;
         } else {
             save();
         }
    }
});

然后是你的保存方法:

private void save(){
    // do your saving work
    saveRequested = false;
}

这仅在您的按钮操作后调用 focusLost 时有效。如果突然顺序正确,此代码将被调用两次 save()。

但同样,以原始方法包装您的 save() 代码应该可以工作,因为保存代码将在处理完所有事件后执行。那是在处理您的按钮单击和 focusLost 事件之后。因为您的 focusLost 代码会立即执行(它没有包含在 invokeLater() 中),所以 focusLost 代码应该始终在您的保存代码之前执行。这并不意味着事件顺序是正确的!但是与事件相关的代码会以正确的顺序执行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-07
    相关资源
    最近更新 更多