Javafx 只要鼠标在工具提示上,就将其保持打开状态

Javafx 只要鼠标在工具提示上,就将其保持打开状态,javafx,tooltip,fxml,Javafx,Tooltip,Fxml,我通过fxml为TableColumn标题创建工具提示,如下所示: <TableColumn> <cellValueFactory> <PropertyValueFactory property="someProperty" /> </cellValueFactory> <graphic> <Label text="Column 1"> <

我通过
fxml
TableColumn
标题创建
工具提示,如下所示:

<TableColumn>
    <cellValueFactory>
        <PropertyValueFactory property="someProperty" />
    </cellValueFactory>
    <graphic>
        <Label text="Column 1">
            <tooltip>
                <Tooltip text="Tooltip text" />
            </tooltip>
        </Label>
    </graphic>
</TableColumn>

这里的问题是
标签
与工具提示不同。因此,如果鼠标位于工具提示上,但不在
标签上,则工具提示将隐藏。我无法将工具提示本身作为悬停目标传递,因为它不是一个
节点

确实是可能的,但它基本上需要删除工具提示的大部分基本功能。我就是这样实现同样的事情的:

首先,我根据基本工具提示制作了一个自定义工具提示(此代码是对类似问题的修改)


如果愿意,您可以编辑该类并使其成为一个具有多个参数的方法,但如果不这样做,则该类确实有效。

我现在使用该类,它可以按预期工作:

public class HoveringTooltip extends Tooltip {

    private Timer timer = new Timer();
    private Map<Object, Boolean> mapHoveringTarget2Hovering = new ConcurrentHashMap<>();
    private final int duration;

    public HoveringTooltip(int duration) {
        super.setAutoHide(false);
        this.duration = duration;
    }

    public void addHoveringTarget(Node object) {

        mapHoveringTarget2Hovering.put(object, false);
        object.setOnMouseEntered(e -> {
            onMouseEntered(object);
        });
        object.setOnMouseExited(e -> {
            onMouseExited(object);
        });
    }

    public void addHoveringTarget(Scene object) {

        mapHoveringTarget2Hovering.put(object, false);
        object.setOnMouseEntered(e -> {
            onMouseEntered(object);
        });
        object.setOnMouseExited(e -> {
            onMouseExited(object);
        });
    }

    @Override
    public void hide() {

        // super.hide();
    }

    public boolean isHovering() {

        return isHoveringProperty().get();
    }

    public BooleanProperty isHoveringProperty() {

        synchronized(mapHoveringTarget2Hovering) {
            for(Entry<Object, Boolean> e : mapHoveringTarget2Hovering.entrySet()) {
                if(e.getValue()) {
                    // if one hovering target is hovering, return true
                    return new ReadOnlyBooleanWrapper(true);
                }
            }
            // no hovering on any target, return false
            return new ReadOnlyBooleanWrapper(false);
        }
    }

    private synchronized void onMouseEntered(Object object) {

        // System.err.println("Mouse entered for " + object + ", canelling timer");
        // stop a potentially running hide timer
        timer.cancel();
        mapHoveringTarget2Hovering.put(object, true);
    }

    private synchronized void onMouseExited(Object object) {

        // System.err.println("Mouse exit for " + object + ", starting timer");
        mapHoveringTarget2Hovering.put(object, false);
        startTimer();
    }

    private void startTimer() {

        timer.cancel();
        timer = new Timer();
        timer.schedule(new TimerTask() {

            @Override
            public void run() {

                Platform.runLater(new Runnable() {

                    @Override
                    public void run() {

                        if(!isHovering())
                            HoveringTooltip.super.hide();
                    }
                });
            }
        }, duration);
    }
}
public类HoveringTooltip扩展了工具提示{
专用计时器=新计时器();
private Map HoveringTarget2 Hovering=新建ConcurrentHashMap();
私人最终整合期;
公共悬停工具提示(int持续时间){
super.setAutoHide(false);
这个。持续时间=持续时间;
}
公共void addHoveringTarget(节点对象){
mapHoveringTarget2Hovering.put(对象,false);
object.SetonMouseCentered(e->{
onmouseintered(对象);
});
object.setOnMouseExited(e->{
onMouseExited(对象);
});
}
公共void addHoveringTarget(场景对象){
mapHoveringTarget2Hovering.put(对象,false);
object.SetonMouseCentered(e->{
onmouseintered(对象);
});
object.setOnMouseExited(e->{
onMouseExited(对象);
});
}
@凌驾
公共空间隐藏(){
//super.hide();
}
公共布尔值Ishoring(){
返回isHoveringProperty().get();
}
公共布尔属性IsOveringProperty(){
已同步(MapHoveringTarget2悬停){
对于(条目e:mapHoveringTarget2Hovering.entrySet()){
if(如getValue()){
//如果一个悬停目标正在悬停,则返回true
返回新的ReadOnlyBooleanWrapper(true);
}
}
//禁止在任何目标上悬停,返回false
返回新的ReadOnlyBooleanWrapper(false);
}
}
已输入的专用同步void(对象){
//System.err.println(“为“+object+”输入鼠标,canelling定时器”);
//停止可能正在运行的隐藏计时器
timer.cancel();
mapHoveringTarget2Hovering.put(对象,true);
}
私有同步的void onMouseExited(对象){
//System.err.println(“鼠标退出“+对象+”,启动计时器”);
mapHoveringTarget2Hovering.put(对象,false);
startTimer();
}
私有void startTimer(){
timer.cancel();
定时器=新定时器();
timer.schedule(新TimerTask(){
@凌驾
公开募捐{
Platform.runLater(新的Runnable(){
@凌驾
公开募捐{
如果(!isHovering())
HoveringTooltip.super.hide();
}
});
}
},持续时间);
}
}

以下是一种破解工具提示行为的方法:

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.control.Tooltip;
import javafx.util.Duration;

import java.lang.reflect.Field;

/**
 * @author rdeardorff
 */
public class TooltipDelay {

    public static void hackTooltipActivationTimer( Tooltip tooltip, int delay ) {
        hackTooltipTiming( tooltip, delay, "activationTimer" );
    }

    public static void hackTooltipHideTimer( Tooltip tooltip, int delay ) {
        hackTooltipTiming( tooltip, delay, "hideTimer" );
    }

    private static void hackTooltipTiming( Tooltip tooltip, int delay, String property ) {
        try {
            Field fieldBehavior = tooltip.getClass().getDeclaredField( "BEHAVIOR" );
            fieldBehavior.setAccessible( true );
            Object objBehavior = fieldBehavior.get( tooltip );

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

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

谢谢你的回答!不幸的是,我无法打开工具提示。我尝试将TableView设置为hoovering目标,并将工具提示/标签本身设置为hoovering目标。将工具提示本身设置为目标不起作用,因为工具提示不是节点。getGraphic()返回null..@kerner1000它对我来说很好。如果你不想使用延迟功能,你可以简化一下(如果你愿意的话),但我看不出有什么问题。@James\u D,作为悬停目标你会传递什么?@kerner1000当然是你将其设置为工具提示的节点。因此,在您的例子中,表列是图形。@James\u D因此,这是设置为表列图形的标签。但工具提示大于此标签。如果鼠标移离标签但停留在工具提示顶部,则工具提示将消失,因为鼠标不再悬停在标签上。
    DelayedTooltip beakerTip = new DelayedTooltip();
    beakerTip.setDuration(999999);
    beakerTip.setText("Science from Base: 12");
    beakerTip.isHoveringTargetPrimary(beakerView);
        beakerTip.isHoveringTargetSecondary(beakerTip.geoGraphic());
public class HoveringTooltip extends Tooltip {

    private Timer timer = new Timer();
    private Map<Object, Boolean> mapHoveringTarget2Hovering = new ConcurrentHashMap<>();
    private final int duration;

    public HoveringTooltip(int duration) {
        super.setAutoHide(false);
        this.duration = duration;
    }

    public void addHoveringTarget(Node object) {

        mapHoveringTarget2Hovering.put(object, false);
        object.setOnMouseEntered(e -> {
            onMouseEntered(object);
        });
        object.setOnMouseExited(e -> {
            onMouseExited(object);
        });
    }

    public void addHoveringTarget(Scene object) {

        mapHoveringTarget2Hovering.put(object, false);
        object.setOnMouseEntered(e -> {
            onMouseEntered(object);
        });
        object.setOnMouseExited(e -> {
            onMouseExited(object);
        });
    }

    @Override
    public void hide() {

        // super.hide();
    }

    public boolean isHovering() {

        return isHoveringProperty().get();
    }

    public BooleanProperty isHoveringProperty() {

        synchronized(mapHoveringTarget2Hovering) {
            for(Entry<Object, Boolean> e : mapHoveringTarget2Hovering.entrySet()) {
                if(e.getValue()) {
                    // if one hovering target is hovering, return true
                    return new ReadOnlyBooleanWrapper(true);
                }
            }
            // no hovering on any target, return false
            return new ReadOnlyBooleanWrapper(false);
        }
    }

    private synchronized void onMouseEntered(Object object) {

        // System.err.println("Mouse entered for " + object + ", canelling timer");
        // stop a potentially running hide timer
        timer.cancel();
        mapHoveringTarget2Hovering.put(object, true);
    }

    private synchronized void onMouseExited(Object object) {

        // System.err.println("Mouse exit for " + object + ", starting timer");
        mapHoveringTarget2Hovering.put(object, false);
        startTimer();
    }

    private void startTimer() {

        timer.cancel();
        timer = new Timer();
        timer.schedule(new TimerTask() {

            @Override
            public void run() {

                Platform.runLater(new Runnable() {

                    @Override
                    public void run() {

                        if(!isHovering())
                            HoveringTooltip.super.hide();
                    }
                });
            }
        }, duration);
    }
}
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.control.Tooltip;
import javafx.util.Duration;

import java.lang.reflect.Field;

/**
 * @author rdeardorff
 */
public class TooltipDelay {

    public static void hackTooltipActivationTimer( Tooltip tooltip, int delay ) {
        hackTooltipTiming( tooltip, delay, "activationTimer" );
    }

    public static void hackTooltipHideTimer( Tooltip tooltip, int delay ) {
        hackTooltipTiming( tooltip, delay, "hideTimer" );
    }

    private static void hackTooltipTiming( Tooltip tooltip, int delay, String property ) {
        try {
            Field fieldBehavior = tooltip.getClass().getDeclaredField( "BEHAVIOR" );
            fieldBehavior.setAccessible( true );
            Object objBehavior = fieldBehavior.get( tooltip );

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

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