问题
I want to highlight parts of text displayed in a JavaFX
TableView
. Till now i'm using Text
objects in TextFlow
objects. To highlight specific parts in a text i am using tags to cut the text in parts (javafx.scene.text
objects) to highlight or not to highlight with the following code.
col3.setCellValueFactory(new PropertyValueFactory<RegexMatch, String>("text"));
col3.setCellFactory(new Callback<TableColumn, TableCell>() {
@Override
public TableCell call(TableColumn param) {
TableCell cell = new TableCell() {
@Override
protected void updateItem(Object text, boolean empty) {
if (text != null && text instanceof String) {
String str = (String) text;
TextFlow flow = new TextFlow();
if (txtSearchField.getText().length() > 3 && str.contains(HIGHLIGHT_START)) {
// Something to highlight
flow.getChildren().clear();
while (str.contains(HIGHLIGHT_START)) {
// First part
Text starttext = new Text(str.substring(0, str.indexOf(HIGHLIGHT_START)));
starttext.setWrappingWidth(Double.MAX_VALUE);
flow.getChildren().add(starttext);
str = str.substring(str.indexOf(HIGHLIGHT_START) + HIGHLIGHT_START.length(), str.length());
// Part to highlight
Text highlightedText = new Text(str.substring(0, str.indexOf(HIGHLIGHT_END)));
highlightedText.setStyle("-fx-text-background-color: yellow;");
highlightedText.setFill(Color.BLUE);
highlightedText.setWrappingWidth(Double.MAX_VALUE);
flow.getChildren().add(highlightedText);
// Last part
str = str.substring(str.indexOf(HIGHLIGHT_END) + HIGHLIGHT_END.length(), str.length());
if (!str.contains(HIGHLIGHT_START)) {
Text endtext = new Text(str);
endtext.setWrappingWidth(Double.MAX_VALUE);
flow.getChildren().add(endtext);
}
}
}else if (txtSearchField.getText().length() < 1) {
// Remove former highlightings and show simple text
str = str.replaceAll(HIGHLIGHT_START, "");
str = str.replaceAll(HIGHLIGHT_END, "");
flow.getChildren().clear();
Text textModule = new Text(str);
textModule.setWrappingWidth(Double.MAX_VALUE);
flow.getChildren().add(textModule);
} else {
// show simple text
flow.getChildren().clear();
Text textModule = new Text(str);
textModule.setWrappingWidth(Double.MAX_VALUE);
flow.getChildren().add(textModule);
}
flow.setPrefHeight(bigIcons ? BIG_SIZE : SMALL_SIZE);
setGraphic(flow);
}
}
};
return cell;
}
});
Unfortunately the background highlighting does not work and i have strange linebreaks as shown in the picture. The text does not contain any linebreaks.
![](https://i0.wp.com/i.stack.imgur.com/Wo0HF.jpg)
(Sorry for the picture quality, its a real screenshot :))
Any help is appreciated.
Solution
As @eckig suggested, using multiple Labels
in a HBox
is an good idea, because each 'Label' can have its own background color and you can use as much Labels
in line in a HBox
as needed:
col3.setCellValueFactory(new PropertyValueFactory<RegexMatch, String("excerptLineTable"));
col3.setCellFactory(new Callback<TableColumn, TableCell>() {
@Override
public TableCell call(TableColumn param) {
TableCell cell = new TableCell() {
@Override
protected void updateItem(Object text, boolean empty) {
if (text != null && text instanceof String) {
HBox hbox = new HBox();
String str = (String) text;
if (txtSearchField.getText().length() > 3 && str.contains(HIGHLIGHT_START)) {
// Something to highlight
hbox.getChildren().clear();
while (str.contains(HIGHLIGHT_START)) {
// First part
Label label = new Label(str.substring(0, str.indexOf(HIGHLIGHT_START)));
hbox.getChildren().add(label);
str = str.substring(str.indexOf(HIGHLIGHT_START) + HIGHLIGHT_START.length(), str.length());
// Part to highlight
Label label2 = new Label(str.substring(0, str.indexOf(HIGHLIGHT_END)));
label2.setStyle("-fx-background-color: blue;");
hbox.getChildren().add(label2);
// Last part
str = str.substring(str.indexOf(HIGHLIGHT_END) + HIGHLIGHT_END.length(), str.length());
if (!str.contains(HIGHLIGHT_START)) {
Label label3 = new Label(str);
hbox.getChildren().add(label3);
}
}
} else if (txtSearchField.getText().length() < 1) {
// Remove former highlightings and show simple text
str = str.replaceAll(HIGHLIGHT_START, "");
str = str.replaceAll(HIGHLIGHT_END, "");
hbox.getChildren().clear();
Label label = new Label(str);
hbox.getChildren().add(label);
} else {
// show simple text
hbox.getChildren().clear();
Label label = new Label(str);
hbox.getChildren().add(label);
}
setGraphic(hbox);
}
}
};
return cell;
}
});
回答1:
After double reading your problem I think we can resume this as follows:
- A rather long text in a TableColumn
- The user should be able to filter this text
- Each text-fragment which matches the current search term is to be highlighted.
Now you have two options:
- Create a HBox and add Labels containg the text fragments. A Label extends Region and so may have a background color.
- Use TextFlow and add Texts as text fragments. There you can "only" change the foreground color.
Ah and before I forget: To disable TextFlow text wrapping you must not call textModule.setWrappingWidth(Double.MAX_VALUE);
but instead textModule.setPrefWidth(Double.MAX_VALUE);
And another hint: Try to recycle as much as possible. Recreating the whole TextFlow on each update is not really a good idea (instead store it as member variable in the cell).
回答2:
According to this, Text
doesn't have background stylable properties, so that's why changing -fx-text-background-color
doesn't have any effect on it.
One possible workaround for this is wrapping this text node in an HBox
, where you can easily set its background to yellow. But this has several drawbacks, such as the textflow losing the ability to manage the content.
If you really need highlighting, other possible solution could be styling the background of the textflow, by finding the bounds of the text, and setting the proper insets.
This is an example I've quickly done with Scenic Builder and some css:
.textflow {
-fx-background-color: yellow;
-fx-background-insets: 2 139.3 10 200;
}
![](https://www.eimg.top/images/2020/03/21/8b6b03d1b47e91d8470dfcff9abb726f.png)
I've taken into account the width of the textflow (510) and the widths of the different text nodes using local bounds: 200, 170.7 and 129. So the right inset is 510-200-170.7=139.3 and the left inset is given from the first node width, 200.
Now the challenge is adapt this in your method...
来源:https://stackoverflow.com/questions/26906810/javafx-tableview-with-highlighted-text