只要鼠标悬停在工具提示上,它就会保持打开状态
Keep Tooltip open as long as mouse is over it
我通过 fxml
为 TableColumn
header 创建了一个 Tooltip
,如下所示:
<TableColumn>
<cellValueFactory>
<PropertyValueFactory property="someProperty" />
</cellValueFactory>
<graphic>
<Label text="Column 1">
<tooltip>
<Tooltip text="Tooltip text" />
</tooltip>
</Label>
</graphic>
</TableColumn>
如果我将鼠标移到工具提示上,我想保持工具提示打开。最后,我希望在工具提示文本中有可点击的链接(就像 Eclipse JavaDoc 工具提示一样)。
这可能吗?
编辑:
考虑到答案,我现在正在尝试以下操作:
Label label = new Label();
label.setText("test text");
DelayedTooltip beakerTip = new DelayedTooltip();
beakerTip.setDuration(3000);
beakerTip.setText("Science from Base: 12");
beakerTip.isHoveringTarget(label);
Tooltip tooltip = new Tooltip();
tooltip.setText("test tooltip text");
label.setTooltip(beakerTip);
myTableColumn.setGraphic(label);
这里的问题是 label
与工具提示不一样。因此,如果鼠标位于工具提示上方但未位于 label
上方,则工具提示将被隐藏。我无法将工具提示本身作为悬停目标传递,因为它不是 Node
.
确实有可能,但它基本上涉及工具提示的大部分基本功能。这就是我实现同样事情的方式:
首先,我根据基本工具提示制作了一个自定义工具提示(此代码是对类似问题的修改)
public class DelayedTooltip extends Tooltip {
private int duration = 0;
private BooleanProperty isHoveringPrimary = new SimpleBooleanProperty(false);
private BooleanProperty isHoveringSecondary = new SimpleBooleanProperty(false);
public void setDuration(int d) {
duration = d;
}
public BooleanProperty isHoveringPrimaryProperty()
{
return isHoveringPrimary;
}
public BooleanProperty isHoveringSecondaryProperty()
{
return isHoveringSecondary;
}
public void isHoveringTargetPrimary(Node node){
node.setOnMouseEntered(e -> isHoveringPrimary.set(true));
node.setOnMouseExited(e -> isHoveringPrimary.set(false));
}
//Usually you will use the tooltip here so enter tooltip.getGraphic() for the node.
public void isHoveringTargetSecondary(Node node){
node.setOnMouseEntered(e -> isHoveringTooltip.set(true)):
node.setOnMouseExited(e -> isHoveringTooltip.set(false));
}
@Override
public void hide() {
if(isHoveringPrimary.get()==true || isHoveringTooltip.get()==true)
{
Timeline timeline = new Timeline();
KeyFrame key = new KeyFrame(Duration.millis(duration));
timeline.getKeyFrames().add(key);
timeline.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
DelayedTooltip.super.hide();
}
});
timeline.play();
}
else
{
DelayedTooltip.super.hide();
}
}
}
然后这就是我安装工具提示的方式
DelayedTooltip beakerTip = new DelayedTooltip();
beakerTip.setDuration(999999);
beakerTip.setText("Science from Base: 12");
beakerTip.isHoveringTargetPrimary(beakerView);
beakerTip.isHoveringTargetSecondary(beakerTip.geoGraphic());
如果您愿意,您可以编辑它并将其变成一个具有多个参数的方法,否则,这确实有效。
我现在使用这个 class,它按预期工作:
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();
}
}
}
我通过 fxml
为 TableColumn
header 创建了一个 Tooltip
,如下所示:
<TableColumn>
<cellValueFactory>
<PropertyValueFactory property="someProperty" />
</cellValueFactory>
<graphic>
<Label text="Column 1">
<tooltip>
<Tooltip text="Tooltip text" />
</tooltip>
</Label>
</graphic>
</TableColumn>
如果我将鼠标移到工具提示上,我想保持工具提示打开。最后,我希望在工具提示文本中有可点击的链接(就像 Eclipse JavaDoc 工具提示一样)。 这可能吗?
编辑: 考虑到答案,我现在正在尝试以下操作:
Label label = new Label();
label.setText("test text");
DelayedTooltip beakerTip = new DelayedTooltip();
beakerTip.setDuration(3000);
beakerTip.setText("Science from Base: 12");
beakerTip.isHoveringTarget(label);
Tooltip tooltip = new Tooltip();
tooltip.setText("test tooltip text");
label.setTooltip(beakerTip);
myTableColumn.setGraphic(label);
这里的问题是 label
与工具提示不一样。因此,如果鼠标位于工具提示上方但未位于 label
上方,则工具提示将被隐藏。我无法将工具提示本身作为悬停目标传递,因为它不是 Node
.
确实有可能,但它基本上涉及工具提示的大部分基本功能。这就是我实现同样事情的方式:
首先,我根据基本工具提示制作了一个自定义工具提示(此代码是对类似问题的修改)
public class DelayedTooltip extends Tooltip {
private int duration = 0;
private BooleanProperty isHoveringPrimary = new SimpleBooleanProperty(false);
private BooleanProperty isHoveringSecondary = new SimpleBooleanProperty(false);
public void setDuration(int d) {
duration = d;
}
public BooleanProperty isHoveringPrimaryProperty()
{
return isHoveringPrimary;
}
public BooleanProperty isHoveringSecondaryProperty()
{
return isHoveringSecondary;
}
public void isHoveringTargetPrimary(Node node){
node.setOnMouseEntered(e -> isHoveringPrimary.set(true));
node.setOnMouseExited(e -> isHoveringPrimary.set(false));
}
//Usually you will use the tooltip here so enter tooltip.getGraphic() for the node.
public void isHoveringTargetSecondary(Node node){
node.setOnMouseEntered(e -> isHoveringTooltip.set(true)):
node.setOnMouseExited(e -> isHoveringTooltip.set(false));
}
@Override
public void hide() {
if(isHoveringPrimary.get()==true || isHoveringTooltip.get()==true)
{
Timeline timeline = new Timeline();
KeyFrame key = new KeyFrame(Duration.millis(duration));
timeline.getKeyFrames().add(key);
timeline.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
DelayedTooltip.super.hide();
}
});
timeline.play();
}
else
{
DelayedTooltip.super.hide();
}
}
}
然后这就是我安装工具提示的方式
DelayedTooltip beakerTip = new DelayedTooltip();
beakerTip.setDuration(999999);
beakerTip.setText("Science from Base: 12");
beakerTip.isHoveringTargetPrimary(beakerView);
beakerTip.isHoveringTargetSecondary(beakerTip.geoGraphic());
如果您愿意,您可以编辑它并将其变成一个具有多个参数的方法,否则,这确实有效。
我现在使用这个 class,它按预期工作:
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();
}
}
}