如何动态地仅在被截断的单元格上设置工具提示

huangapple go评论68阅读模式
英文:

How to set tooltip ONLY on truncated cell dynamically

问题

我的目标是在启动时以及运行时在文本被截断的单元格上显示工具提示,这意味着如果调整列的大小,可能会出现或消失一些工具提示。

在我的单元格工厂中,我以为会有一个属性可以绑定或监听,然后简单地将工具提示链接到这个属性,但我没有找到。

这是在cell factory中为每个单元格设置工具提示的代码:

public void updateItem(T item, boolean empty) {
    super.updateItem(item, empty);

    if (empty || item == null) {
        setText(null);
        setGraphic(null);
    } else {
        setText(adapter.getCellContentParser().toString(item));
        setTooltip(new Tooltip(this.getText()));
    }
}

请注意,我在网站上寻找了所有可能的解决方案,比如这些:
https://stackoverflow.com/questions/15683907/javafx-tableviews-cellvalue-is-not-enough-to-display-in-columns-it-will-be-cl#_=_

https://stackoverflow.com/questions/45614922/how-to-show-tooltip-only-on-those-table-cells-which-text-is-ellipsized

当您启动程序时,它们非常好用,但是在调整列大小时不起作用

我是否遗漏了什么? 我觉得真的很奇怪,竟然没有针对这种情况的可观察属性。

英文:

My goal is to display a tooltip on cells where the text is truncated at launch, but also during runtime, which means that if a column is resized, some tool tip may appear or dissapear.

In my cell factory, I thought there will be a property that I could bind or listen to, and simply link a tooltip to this, but I found none.

Here's my code that set a tool tip on every cell in the cell factory:

public void updateItem(T item, boolean empty) {
	super.updateItem(item, empty);

	if (empty || item == null) {
		setText(null);
		setGraphic(null);
	} else {
		setText(adapter.getCellContentParser().toString(item));
        setTooltip(new Tooltip(this.getText());
	}
}

Please note that I look for every possible solution on the site, like these ones :
https://stackoverflow.com/questions/15683907/javafx-tableviews-cellvalue-is-not-enough-to-display-in-columns-it-will-be-cl#_=_

https://stackoverflow.com/questions/45614922/how-to-show-tooltip-only-on-those-table-cells-which-text-is-ellipsized

And they are perfectly fine when you launch your program, but don't work when you resizing your columns.

Is there something I'm missing ? I find it really strange that there is not an observableproperty for this kind of thing.

答案1

得分: 4

以下是代码的翻译部分:

这是一个有点巧妙的方法,但您可以在布局完成后通过搜索单元格下的场景图来检索单元格中的子Text节点,然后为工具提示创建一个绑定,检查单元格的文本是否与文本的文本相同。请注意,我们在这里忽略了单元格上设置的任何图形,因此这只会搜索显示单元格的text属性的Text节点:

setCellFactory(tc -> new TableCell<>() {
    private Text text;
    private Tooltip tooltip = new Tooltip();

    {
        tooltip.textProperty().bind(textProperty());
    }

    @Override
    protected void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);

        if (item == null || empty) {
            setText("");
        } else {
            setText(item);
        }
    }

    @Override
    protected void layoutChildren() {
        super.layoutChildren();
        if (text == null) {
            text = findTextNode(this);
            bindTooltip();
        }
    }

    private void bindTooltip() {
        tooltipProperty().bind(Bindings.createObjectBinding(() -> {
            if (getItem() == null) return null;
            if (getText().equals(text.getText())) return null;
            return tooltip;
        }, text.textProperty(), itemProperty(), textProperty()));
    }

    // 递归搜索子节点图以查找Text节点
    private Text findTextNode(Node root) {
        // 忽略图形及其所有子节点:
        if (root == getGraphic()) {
            return null;
        }
        if (root instanceof Text) {
            return (Text) root;
        }
        if (root instanceof Parent) {
            for (Node node : ((Parent) root).getChildrenUnmodifiable()) {
                Text text = findTextNode(node);
                if (text != null) return text;
            }
        }
        return null;
    }
});

这是一个完整的工作示例:

import java.util.Random;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;

/**
 * JavaFX App
 */
public class App extends Application {

    @Override
    public void start(Stage stage) {

        var table = new TableView<Item>();
        var itemCol = column("Item", Item::nameProperty);
        var valueCol = column("Value", Item::valueProperty);
        table.getColumns().add(itemCol);
        table.getColumns().add(valueCol);

        itemCol.setCellFactory(tc -> new TableCell<>() {
            private Text text;
            private Tooltip tooltip = new Tooltip();

            {
                tooltip.textProperty().bind(textProperty());
            }

            @Override
            protected void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);

                if (item == null || empty) {
                    setText("");
                } else {
                    setText(item);
                }
            }

            @Override
            protected void layoutChildren() {
                super.layoutChildren();
                if (text == null) {
                    text = findTextNode(this);
                    bindTooltip();
                }
            }

            private void bindTooltip() {
                tooltipProperty().bind(Bindings.createObjectBinding(() -> {
                    if (getItem() == null) return null;
                    if (getText().equals(text.getText())) return null;
                    return tooltip;
                }, text.textProperty(), itemProperty(), textProperty()));
            }

            // 递归搜索子节点图以查找Text节点
            private Text findTextNode(Node root) {
                // 忽略图形及其所有子节点:
                if (root == getGraphic()) {
                    return null;
                }
                if (root instanceof Text) {
                    return (Text) root;
                }
                if (root instanceof Parent) {
                    for (Node node : ((Parent) root).getChildrenUnmodifiable()) {
                        Text text = findTextNode(node);
                        if (text != null) return text;
                    }
                }
                return null;
            }
        });

        var rng = new Random();
        for (int i = 1; i <= 40; i++) {
            table.getItems().add(new Item("Item " + i, rng.nextInt(100)));
        }

        var scene = new Scene(new BorderPane(table));
        stage.setScene(scene);
        stage.show();
    }

    public static <S, T> TableColumn<S, T> column(String title, Function<S, Property<T>> prop) {
        TableColumn<S, T> col = new TableColumn<>(title);
        col.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
        return col;
    }

    public static class Item {
        private final StringProperty name = new SimpleStringProperty();
        private final IntegerProperty value = new SimpleIntegerProperty();

        public Item(String name, int value) {
            this.name.set(name);
            this.value.set(value);
        }

        public final StringProperty nameProperty() {
            return name;
        }

        public final IntegerProperty valueProperty() {
            return value;
        }
    }

    public static void main(String[] args) {
        launch();
    }
}
英文:

This is a bit of a hack, but you can retrieve the child Text node in the cell by searching the scene graph below the cell after layout is performed, and then create a binding for the tooltip that checks if the cell's text is the same as the text's text. Note that we ignore any graphic set on the cell here, so this only searches for the Text node displaying the cell's text property:

setCellFactory(tc -&gt; new TableCell&lt;&gt;() {
private Text text ;
private Tooltip tooltip = new Tooltip() ;
{
tooltip.textProperty().bind(textProperty());
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(&quot;&quot;);
} else {
setText(item);
}
}
@Override
protected void layoutChildren() {
super.layoutChildren();
if (text == null) {
text = findTextNode(this);
bindTooltip();
}    			
}
private void bindTooltip() {
tooltipProperty().bind(Bindings.createObjectBinding(() -&gt; {
if (getItem()==null) return null ;
if (getText().equals(text.getText())) return null ;
return tooltip ;
}, text.textProperty(), itemProperty(), textProperty()));
}
// recursively search through child node graph to find the Text node
private Text findTextNode(Node root) {
// Ignore the graphic and all its child nodes:
if (root == getGraphic()) {
return null ;
}
if (root instanceof Text) {
return (Text) root ;
}
if (root instanceof Parent) {
for (Node node : ((Parent)root).getChildrenUnmodifiable()) {
Text text = findTextNode(node);
if (text != null) return text ;
}
}
return null ;
}
});

Here's a complete working example:

import java.util.Random;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
* JavaFX App
*/
public class App extends Application {
@Override
public void start(Stage stage) {
var table = new TableView&lt;Item&gt;();
var itemCol = column(&quot;Item&quot;, Item::nameProperty);
var valueCol = column(&quot;Value&quot;, Item::valueProperty);
table.getColumns().add(itemCol);
table.getColumns().add(valueCol);
itemCol.setCellFactory(tc -&gt; new TableCell&lt;&gt;() {
private Text text ;
private Tooltip tooltip = new Tooltip() ;
{
tooltip.textProperty().bind(textProperty());
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(&quot;&quot;);
} else {
setText(item);
}
}
@Override
protected void layoutChildren() {
super.layoutChildren();
if (text == null) {
text = findTextNode(this);
bindTooltip();
}    			
}
private void bindTooltip() {
tooltipProperty().bind(Bindings.createObjectBinding(() -&gt; {
if (getItem()==null) return null ;
if (getText().equals(text.getText())) return null ;
return tooltip ;
}, text.textProperty(), itemProperty(), textProperty()));
}
// recursively search through child node graph to find the Text node
private Text findTextNode(Node root) {
// Ignore the graphic and all its child nodes:
if (root == getGraphic()) {
return null ;
}
if (root instanceof Text) {
return (Text) root ;
}
if (root instanceof Parent) {
for (Node node : ((Parent)root).getChildrenUnmodifiable()) {
Text text = findTextNode(node);
if (text != null) return text ;
}
}
return null ;
}
});
var rng = new Random();
for (int i = 1 ; i &lt;= 40 ; i++) {
table.getItems().add(new Item(&quot;Item &quot;+i, rng.nextInt(100)));
}
var scene = new Scene(new BorderPane(table));
stage.setScene(scene);
stage.show();
}
public static &lt;S,T&gt;  TableColumn&lt;S,T&gt; column(String title, Function&lt;S, Property&lt;T&gt;&gt; prop) {
TableColumn&lt;S,T&gt; col = new TableColumn&lt;&gt;(title);
col.setCellValueFactory(cellData -&gt; prop.apply(cellData.getValue()));
return col ;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty value = new SimpleIntegerProperty();
public Item(String name, int value) {
this.name.set(name);
this.value.set(value);
}
public final StringProperty nameProperty() {
return name ;
}
public final IntegerProperty valueProperty() {
return value ;
}
}
public static void main(String[] args) {
launch();
}
}

huangapple
  • 本文由 发表于 2020年5月29日 22:35:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/62088393.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定