如何控制JavaFX提示框的延迟?

35
我在使用JavaFX的Tooltip时发现,对于我个人来说,鼠标悬停在某个控件上和工具提示实际出现之间的延迟时间太长了。查看API后发现:

通常情况下,当鼠标移动到控件上时,工具提示会被“激活”。通常在工具提示被“激活”和实际显示之间存在一些延迟。详细信息(如延迟时间等)由皮肤实现留给用户自定义。

经过进一步调查,我没有找到任何控制延迟的可能性。JavaFX CSS参考文档中也没有关于延迟时间的信息,运行getCssMetaData()也没有帮助。
我知道可以通过onMouseEntered(...)onMouseExited(...)手动控制工具提示,但是难道真的没有其他方法吗?或者我错过了一个明显的解决方案吗?
8个回答

51

我通过反射使用以下hack来实现这个功能

public static void hackTooltipStartTiming(Tooltip tooltip) {
    try {
        Field fieldBehavior = tooltip.getClass().getDeclaredField("BEHAVIOR");
        fieldBehavior.setAccessible(true);
        Object objBehavior = fieldBehavior.get(tooltip);

        Field fieldTimer = objBehavior.getClass().getDeclaredField("activationTimer");
        fieldTimer.setAccessible(true);
        Timeline objTimer = (Timeline) fieldTimer.get(objBehavior);

        objTimer.getKeyFrames().clear();
        objTimer.getKeyFrames().add(new KeyFrame(new Duration(250)));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

这对我的需求很好用。而且,作为一个额外的好处,似乎你只需要在整个应用程序中调用它一次,而不是为每个工具提示实例都调用它。 - David
2
奖励:您还可以为工具提示的持续时间执行此操作!字段名称为“hideTimer”,而不是“activationTimer”。 - user3804769
你在使用哪些导入? - SovietFrontier
@David 这个是整个应用程序只需要运行一次吗?我看到的情况并不是这样,所以我认为你可能是错的。(但愿你不是!)它确实需要一个Tooltip实例,因此每个Tooltip都需要进行设置。但另一个问题在于,Tooltip也可以在FXML中创建,而不仅仅是在代码中。 - Manius
@Manius,我的评论已经四年了,很遗憾,我不记得那时候我的代码是什么样子的。我自那以后就没有再使用JavaFX,所以我甚至无法开始尝试回答你。抱歉... - David
显示剩余2条评论

24

从Java 9开始,你可以这样做:

Tooltip tooltip = new Tooltip("A tooltip");
tooltip.setShowDelay(Duration.seconds(3));

还有一个hideDelay属性,用于定义提示框出现后隐藏的延迟时间。默认值为showDelay是1秒,hideDelay是200毫秒。


默认值不是反过来吗?200毫秒肯定不足以阅读任何工具提示,除非隐藏延迟是指自动隐藏之前它停留的总时间…… - Manius
@Manius 根据文档并非如此,尽管我同意这似乎很奇怪。 - James_D

16

9

我发现在上述实现中,有时仍然会出现我无法解释的延迟。

以下方法对我起作用,并完全消除了延迟:

public static void bindTooltip(final Node node, final Tooltip tooltip){
   node.setOnMouseMoved(new EventHandler<MouseEvent>(){
      @Override  
      public void handle(MouseEvent event) {
         // +15 moves the tooltip 15 pixels below the mouse cursor;
         // if you don't change the y coordinate of the tooltip, you
         // will see constant screen flicker
         tooltip.show(node, event.getScreenX(), event.getScreenY() + 15);
      }
   });  
   node.setOnMouseExited(new EventHandler<MouseEvent>(){
      @Override
      public void handle(MouseEvent event){
         tooltip.hide();
      }
   });
}

这将会显示一系列的工具提示,一个接一个地出现,而且不会隐藏任何东西。这些工具提示不会从我的屏幕上消失,直到我杀死该进程:)) - Madrugada
原因是使用了鼠标事件MouseMoved。我将其更改为MouseEntered。这样做的缺点是它不会跟随光标移动。但你可以将MouseMoved函数更改为MouseEntered,并在mouseMoved函数中设置tooltip.setAnchorX(event.getScreenX()),Y也是如此。我建议进行编辑。如果还没有,请回复我,我会发送给你代码。 - JoschJava
请不要建议修改,请在此处发布您自己的答案。并在答案中添加链接,解释这个答案有什么问题,您的答案有哪些改进。 - Shashanth
你使用了哪些导入? - SovietFrontier

6

5

扩展工具提示或将其放在应用程序类上。(仅适用于Java8)

    /**
     * <p>
     * Hack TooltipBehavior 
     */
    static {
        try {
            Tooltip obj = new Tooltip();
            Class<?> clazz = obj.getClass().getDeclaredClasses()[1];
            Constructor<?> constructor = clazz.getDeclaredConstructor(
                    Duration.class,
                    Duration.class,
                    Duration.class,
                    boolean.class);
            constructor.setAccessible(true);
            Object tooltipBehavior = constructor.newInstance(
                    new Duration(250),  //open
                    new Duration(5000), //visible
                    new Duration(200),  //close
                    false);
            Field fieldBehavior = obj.getClass().getDeclaredField("BEHAVIOR");
            fieldBehavior.setAccessible(true);
            fieldBehavior.set(obj, tooltipBehavior);
        }
        catch (Exception e) {
            Logger.error(e);
        }
    }

4
在JavaFX(Java 8)中,可以找到一个类似的方法,基于一个单独的效用方法来设置工具提示的全局时间参数。这里是链接:https://gist.github.com/darmbrust/9559744d1b1dada434a3 - Marco

3

你好,我还没有达到50个声望,无法发表评论,但我想更正Bruno Pado所提供的答案。 他发布的代码在JDK 8u121中无法运行。问题出在它访问的声明字段上。修复方法很简单,将索引从1改为0。以下是可运行的代码:

/**
 * <p>
 * Hack TooltipBehavior 
 */
static {
    try {
        Tooltip obj = new Tooltip();
        Class<?> clazz = obj.getClass().getDeclaredClasses()[0];
        Constructor<?> constructor = clazz.getDeclaredConstructor(
                Duration.class,
                Duration.class,
                Duration.class,
                boolean.class);
        constructor.setAccessible(true);
        Object tooltipBehavior = constructor.newInstance(
                new Duration(250),  //open
                new Duration(5000), //visible
                new Duration(200),  //close
                false);
        Field fieldBehavior = obj.getClass().getDeclaredField("BEHAVIOR");
        fieldBehavior.setAccessible(true);
        fieldBehavior.set(obj, tooltipBehavior);
    }
    catch (Exception e) {
        Logger.error(e);
    }
}

希望这能帮助到想要编辑工具提示延迟时间的人,同时我们也在等待JFX9。

1
我认为这里需要注意的是,依赖于getDeclaredClasses返回的顺序可能不是一个好主意,因为它在二进制版本之间并不总是一致的。如果你知道你要查找的类,那么你可以通过过滤来实现。 - apokryfos

3

自从JavaFX 2以来,工具提示的行为在类Tooltip内部由静态字段BEHAVIOR管理,该字段不能使用特定的公共方法进行修改。因此,如果您现在不想重新发明轮子或等待Java 9,唯一的方法是使用反射API,如下所示:

/**
 * Hack allowing to modify the default behavior of the tooltips.
 * @param openDelay The open delay, knowing that by default it is set to 1000.
 * @param visibleDuration The visible duration, knowing that by default it is set to 5000.
 * @param closeDelay The close delay, knowing that by default it is set to 200.
 * @param hideOnExit Indicates whether the tooltip should be hide on exit, 
 * knowing that by default it is set to false.
 */
private static void updateTooltipBehavior(double openDelay, double visibleDuration,
    double closeDelay, boolean hideOnExit) {
    try {
        // Get the non public field "BEHAVIOR"
        Field fieldBehavior = Tooltip.class.getDeclaredField("BEHAVIOR");
        // Make the field accessible to be able to get and set its value
        fieldBehavior.setAccessible(true);
        // Get the value of the static field
        Object objBehavior = fieldBehavior.get(null);
        // Get the constructor of the private static inner class TooltipBehavior
        Constructor<?> constructor = objBehavior.getClass().getDeclaredConstructor(
            Duration.class, Duration.class, Duration.class, boolean.class
        );
        // Make the constructor accessible to be able to invoke it
        constructor.setAccessible(true);
        // Create a new instance of the private static inner class TooltipBehavior
        Object tooltipBehavior = constructor.newInstance(
            new Duration(openDelay), new Duration(visibleDuration),
            new Duration(closeDelay), hideOnExit
        );
        // Set the new instance of TooltipBehavior
        fieldBehavior.set(null, tooltipBehavior);
    } catch (Exception e) {
        throw new IllegalStateException(e);
    }
}

这绝对是最好的Java 8(仅限)解决方案,做得非常出色。 - Manius

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