英文:
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#_=_
当您启动程序时,它们非常好用,但是在调整列大小时不起作用。
我是否遗漏了什么? 我觉得真的很奇怪,竟然没有针对这种情况的可观察属性。
英文:
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#_=_
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 -> 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()));
}
// 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<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()));
}
// 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 <= 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();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论