【问题标题】:SWT.KeyUp triggers too oftenSWT.KeyUp 触发太频繁
【发布时间】:2015-12-01 10:18:28
【问题描述】:

我在理解 SWT 中的关键事件时遇到了一些麻烦。我的程序在 Windows 8.1 + Java 8 下运行。
例如我有一个文本字段并尝试处理 KeyUp:

textField.addListener(SWT.KeyUp, new Listener() {
  @Override
  public void handleEvent(Event e) {
  }
});

在 handleEvent 方法中,我将 e.doit 设置为 false,然后尝试区分箭头键、空格键或 ALT + 字母等的某些组合。

我有点疑惑,因为我的应用程序执行的任务不正确。于是我调试了coe,发现handleEvent方法每次都用不同的事件对象执行了3次。
这不好,因为在这种情况下,几乎不可能让它以预期的方式工作 - 每个调用都可以替换 f.e.上一次调用的属性值。
而且不知道是什么原因:我按了一个键,handler方法被调用了3次。为什么?这对我来说有点不合逻辑......

+++ 已编辑 +++

package mytest;

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class Main {
  private static int i = 0;

  public static void main(String[] args) {
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());
    Text textField = new Text(shell, SWT.BORDER | SWT.READ_ONLY | SWT.SINGLE);
    textField.addListener(SWT.KeyUp, new Listener() {
      @Override
      public void handleEvent(Event e) {
        e.doit = false;
        System.out.println("Call #" + (++i) + ": keyCode=" + e.keyCode);
      }
    });

    shell.pack();
    shell.open();
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch())
        display.sleep();
    }
    display.dispose();
  }
}

您可以运行这个小示例,然后按 f.e。 ALT+A、CTRL+I 或 ARROW_DOWN,您会看到每次按下都会经常调用句柄方法,这不是我对按键监听器的期望!

【问题讨论】:

  • 这不是我在快速测试中看到的。您需要向我们展示Minimal, Complete, and Verifiable example
  • 好的,我贴了一个小代码示例来演示这个问题;请记住,我在 Windows 下运行它。
  • 在我的 Mac 上,我看到了两个事件,这是我所期望的 - 每个键都被释放。
  • @greg: 不。例如,如果我按 CTRL+o,那么我希望在 e.stateMask 中找到 SWT.CTRL,在同一事件对象中的 e.keyCode 中找到 'o' char。这就是为什么我希望调用一次处理程序方法,而不是两次或三次左右。否则我必须将每个方法调用的关键代码组合在一个全局变量中以获得完整的快捷方式,这会很奇怪。
  • 如果我的回答与您的期望不符,请提供您所看到的准确输出以及输入内容。提供minimal reproducible example 代码非常有用,接下来要明确输入和输出。

标签: java swt keylistener


【解决方案1】:

解决方案

你要观察的是事件中的其他字段:

stateMask:

当时键盘修饰键和鼠标掩码的状态 事件已生成。

keyLocation:

根据事件,keyCode指定的key的位置 或性格。该字段的可能值为 SWT.LEFT, SWT.RIGHT、SWT.KEYPAD 或 SWT.NONE 代表主键盘 区域。

位置字段可用于区分关键事件 具有相同的键码和字符,但由不同的生成 键盘上的键。例如,带有按键代码的按键按下事件 等于 SWT.SHIFT 可以通过左右移位生成 键盘上的键。

位置字段只能用于确定 当前事件中的键码或字符。它不包括 有关状态掩码中修饰符位置的信息。

更新示例

正确的、预期的行为是在每个键被释放时都会收到通知。将问题中的 println 替换为:

System.out.println("Call #" + (++i) + ": keyCode=" + e.keyCode + " stateMask=" + e.stateMask
        + " ctrlPressed=" + ((e.stateMask & SWT.CTRL) != 0) + " shiftPressed="
                + ((e.stateMask & SWT.SHIFT) != 0)+ " altPressed="
                        + ((e.stateMask & SWT.ALT) != 0));

你应该观察正确的行为。 stateMask 详细说明状态(例如按下控制键)。

使用额外的打印更新您的代码,这就是您在控制台中看到的内容:

对于一个

Call #11: keyCode=97 stateMask=0 ctrlPressed=false shiftPressed=false altPressed=false

对于ctrl+a,先释放a

Call #14: keyCode=97 stateMask=262144 ctrlPressed=true shiftPressed=false altPressed=false
Call #15: keyCode=262144 stateMask=262144 ctrlPressed=true shiftPressed=false altPressed=false

对于ctrl+a,先发布ctrl

Call #16: keyCode=262144 stateMask=262144 ctrlPressed=true shiftPressed=false altPressed=false
Call #17: keyCode=97 stateMask=0 ctrlPressed=false shiftPressed=false altPressed=false

对于ctrl+shift+alt+a,发布a第一:

Call #19: keyCode=97 stateMask=458752 ctrlPressed=true shiftPressed=true altPressed=true
Call #20: keyCode=65536 stateMask=458752 ctrlPressed=true shiftPressed=true altPressed=true
Call #21: keyCode=131072 stateMask=393216 ctrlPressed=true shiftPressed=true altPressed=false
Call #22: keyCode=262144 stateMask=262144 ctrlPressed=true shiftPressed=false altPressed=false

为什么?

有很多地方您想知道是否只是按下和/或释放了 ctrl 键。考虑将鼠标悬停在 Eclipse 中的方法或变量上并按下/释放 ctrl

更新

根据 cmets 中提供的其他信息,OP 希望允许用户创建加速器。

jface,SWT之上的层已经有这样一个类KeySequenceText(代码:in git),

SWT 文本小部件的包装器,用于捕获文字按键 并将它们转换为键序列以进行显示。有两种类型 显示的击键数:完整和不完整。一套完整的 击键是有自然键的,而不完整的则没有 自然键。不完整的击键只会显示,直到它们 完成或释放它们的组件按键。

显然我会建议重复使用它而不是旋转一个新的。它还不到 1000 行已经大量使用的代码,因此可能得到了很好的锻炼并且(大部分!)没有错误。

由于您在 Eclipse 平台之外使用 SWT,如果您确实想采用这种方法,您也可以使用 jface outside the platform

【讨论】:

  • 好了,keyCode、stateMask、keyLocation这几个字段的含义就很清楚了。但我不明白的是为什么处理程序方法可以调用不止一次。在您的示例中,您说这取决于首先释放哪个键,但是不可能找到正确的快捷方式,不是吗?例如,我在先释放 Ctrl 的情况下按 Ctrl+a。在这种情况下,我永远无法在第一个方法调用中检查 Ctrl+a。而且我不知道是否会有第二次或第三次电话。
  • 还有一个问题是事件“覆盖”了自己。我在第一个处理程序调用中使用 keyCode 和 stateMask,认为它是正确的快捷方式并将其存储在数据库中 - 但不,处理程序方法被一次又一次地调用,并且每次我得到(可能)错误的快捷方式。我期待的是我按下按键,处理程序方法被调用一次,并且事件对象包含我需要的所有信息。
  • 如果你想知道按下了什么,也听一下 Key Down 事件!例如,监听(key down)keyCode == a 和 stateMask == alt,当该状态命中然后在 key up 事件上运行你的事件?
  • 嗯,如果我错了,请纠正我,但是 keydown 的方法调用计数与 keyup 相同。所以我仍然有同样的问题。假设我想让用户更改菜单项的快捷方式。所以我想得到按下(或释放)的键,如果每次都用不同的事件对象频繁调用事件方法,我应该怎么做?
  • 出于好奇,您是单独使用 SWT 还是使用 RCP。我问的原因是 RCP 为您解决了键盘快捷键/菜单键组合等问题,即不是您必须自己重新实现的东西。当然,您可能有充分的理由重新实现这些东西。
【解决方案2】:

通常,使用 addListener 添加的侦听器几乎在每次呼吸时都会被非常频繁地调用。对于关键事件,请使用

textField.addKeyListener( new KeyAdapter() 
{ 
   public void keyPressed( Event e ) 
} );

【讨论】:

  • 您确实意识到addKeyListener() 在内部只是为适当的事件调用addListener()?所以没有区别。 Here's the source code
  • 看起来它在内部使用 addListener(),但根据我使用 swt 监听器的经验,添加特定监听器总是有效的,而不是添加普通监听器
  • @svasa 恕我直言,它可以使代码更具可读性,并且使用类型化的侦听器更不容易出错,但从根本上说,在每个被触发的事件上都有一个额外的翻译层。当然,除非我知道(已经测试过)性能,否则我更喜欢可读性。而且Baz是对的,调用的次数没有区别。
  • @svasa 你有任何数据支持这个说法吗?我不明白为什么它会更有效率。
猜你喜欢
  • 1970-01-01
  • 2012-10-21
  • 1970-01-01
  • 2023-04-02
  • 2019-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-11
相关资源
最近更新 更多