javafx 中 table 行的删除线
Strikethough on table row in javafx
我想在用户删除某行时将其更新为删除线。我是 javafx 的新手,一直在寻找但没有运气。
donationsTable.setRowFactory(tv -> {
TableRow<Donation> row = new TableRow<Donation>() {
// to force updateItem called
@Override
protected boolean isItemChanged(Donation d,
Donation d2) {
return true;
}
@Override
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
if (d == null) {
setStyle("");
} else if (d.getAction().equals(Donation.DELETE_DONATION)) {
setStyle("delete-row");
} else if (d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
} else {
setStyle("");
}
}
};
row.setOnMouseClicked(event -> {
deleteDonation.setDisable(false);
});
return row;
});
新捐赠的大胆作品,但我无法让删除线生效。我确实看到它需要在文本上设置,而不是行所以我的 css 是:
.delete-row .text {
-fx-strikethrough: true;
}
但是,我收到一条警告:警告 CSS 解析 '*{delete-row} 时出错:[1,12] 处应有冒号
我对css只有很基础的了解。这是我在其他答案中看到的,但我不明白为什么它对我不起作用。
非常感谢任何帮助。
根据James_D的建议,我修改了updateItem:
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete, d != null && d.getAction().equals(Donation.DELETE_DONATION));
PseudoClass add = PseudoClass.getPseudoClass("add-row");
pseudoClassStateChanged(add, d != null && d.getAction().equals(Donation.NEW_DONATION));
}
css 有
.table-row-cell:delete-row .text {
-fx-strikethrough: true;
}
.table-row-cell:add-row {
-fx-font-weight: bold;
}
删除线仍然无效,粗体停止工作。
setStyle
方法将在 Node
上设置内联样式;此样式采用 CSS 规则的形式。这就是你对粗体的处理:
if (d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
}
要将 CSS class 添加到节点的 classes 列表中,获取节点的 CSS classes 列表getStyleClass()
,并操纵它。
这里你必须小心一点,因为列表可以包含相同值的多个副本,而且你无法控制 updateItem()
被调用的次数以及调用 [=22= 的次数]s 作为参数。最好的选择是删除 class delete-row
的所有实例,并在正确的条件下添加一个:
@Override
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
getStyleClass().removeAll(Collections.singleton("delete-row"));
if (d == null) {
setStyle("");
} else if (d.getAction().equals(Donation.DELETE_DONATION)) {
setStyle("");
getStyleClass().add("delete-row");
} else if (d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
} else {
setStyle("");
}
}
另一种选择是使用 CSS 伪 class 代替:
@Override
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete, d != null && d.getAction().equals(Donation.DELETE_DONATION));
if (d != null && d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
} else {
setStyle("");
}
}
和
.table-row-cell:delete-row .text {
-fx-strikethrough: true;
}
在这种情况下,为了保持一致性,我可能也会将 NEW_DONATION
样式重构为伪class。
这是一个使用伪classes 的完整示例。请注意,我将 CSS 更改为粗体(据我了解,使用 font-weight
取决于系统为 currently-selected 字体提供粗体字体;使用通用的东西(sans-serif
) 使用 -fx-font
规则更可靠。)
Donation.java
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Donation {
public enum Action { NEW_DONATION, DELETE_DONATION, NO_ACTION }
private final StringProperty name = new SimpleStringProperty() ;
private final ObjectProperty<Action> action = new SimpleObjectProperty<>() ;
public Donation(String name, Action action) {
setName(name);
setAction(action);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final ObjectProperty<Action> actionProperty() {
return this.action;
}
public final Action getAction() {
return this.actionProperty().get();
}
public final void setAction(final Action action) {
this.actionProperty().set(action);
}
}
App.java
import java.util.Random;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage stage) {
TableView<Donation> table = new TableView<>();
table.setRowFactory(tv -> {
TableRow<Donation> row = new TableRow<>() {
@Override
protected void updateItem(Donation donation, boolean empty) {
super.updateItem(donation, empty);
PseudoClass add = PseudoClass.getPseudoClass("add-row");
pseudoClassStateChanged(add,
donation != null && donation.getAction() == Donation.Action.NEW_DONATION);
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete,
donation != null && donation.getAction() == Donation.Action.DELETE_DONATION);
}
};
return row ;
});
Random rng = new Random();
for (int i = 1 ; i <= 40 ; i++) {
table.getItems().add(new Donation("Donation "+i, Donation.Action.values()[rng.nextInt(3)]));
}
table.getColumns().add(column("Donation", Donation::nameProperty));
table.getColumns().add(column("Action", Donation::actionProperty));
BorderPane root = new BorderPane(table);
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
private static <S,T> TableColumn<S,T> column(String name, Function<S, Property<T>> prop) {
TableColumn<S,T> col = new TableColumn<>(name);
col.setCellValueFactory(data -> prop.apply(data.getValue()));
return col ;
}
public static void main(String[] args) {
launch();
}
}
style.css:
.table-row-cell:delete-row .text {
-fx-strikethrough: true;
}
.table-row-cell:add-row {
/* -fx-font-weight: bold; */
-fx-font: bold 1em sans-serif ;
}
更新:
如果其中一列未观察到 属性 确定 table 行的样式(例如,在上面的示例中,“action”列不存在),您需要安排行来观察 属性 本身。这有点棘手,因为该行被不同的 table 项重复使用,因此您需要在发生这种情况时从正确的 属性 添加和删除侦听器。这看起来像:
table.setRowFactory(tv -> {
TableRow<Donation> row = new TableRow<>() {
// Listener that updates style when the actionProperty() changes
private final ChangeListener<Donation.Action> listener =
(obs, oldAction, newAction) -> updateStyle();
{
// make sure listener above is registered
// with the correct actionProperty()
itemProperty().addListener((obs, oldDonation, newDonation) -> {
if (oldDonation != null) {
oldDonation.actionProperty().removeListener(listener);
}
if (newDonation != null) {
newDonation.actionProperty().addListener(listener);
}
});
}
@Override
protected void updateItem(Donation donation, boolean empty) {
super.updateItem(donation, empty);
updateStyle();
}
private void updateStyle() {
Donation donation = getItem();
PseudoClass add = PseudoClass.getPseudoClass("add-row");
pseudoClassStateChanged(add, donation != null && donation.getAction() == Donation.Action.NEW_DONATION);
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete, donation != null && donation.getAction() == Donation.Action.DELETE_DONATION);
}
};
return row ;
});
我想在用户删除某行时将其更新为删除线。我是 javafx 的新手,一直在寻找但没有运气。
donationsTable.setRowFactory(tv -> {
TableRow<Donation> row = new TableRow<Donation>() {
// to force updateItem called
@Override
protected boolean isItemChanged(Donation d,
Donation d2) {
return true;
}
@Override
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
if (d == null) {
setStyle("");
} else if (d.getAction().equals(Donation.DELETE_DONATION)) {
setStyle("delete-row");
} else if (d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
} else {
setStyle("");
}
}
};
row.setOnMouseClicked(event -> {
deleteDonation.setDisable(false);
});
return row;
});
新捐赠的大胆作品,但我无法让删除线生效。我确实看到它需要在文本上设置,而不是行所以我的 css 是:
.delete-row .text {
-fx-strikethrough: true;
}
但是,我收到一条警告:警告 CSS 解析 '*{delete-row} 时出错:[1,12] 处应有冒号 我对css只有很基础的了解。这是我在其他答案中看到的,但我不明白为什么它对我不起作用。
非常感谢任何帮助。
根据James_D的建议,我修改了updateItem:
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete, d != null && d.getAction().equals(Donation.DELETE_DONATION));
PseudoClass add = PseudoClass.getPseudoClass("add-row");
pseudoClassStateChanged(add, d != null && d.getAction().equals(Donation.NEW_DONATION));
}
css 有
.table-row-cell:delete-row .text {
-fx-strikethrough: true;
}
.table-row-cell:add-row {
-fx-font-weight: bold;
}
删除线仍然无效,粗体停止工作。
setStyle
方法将在 Node
上设置内联样式;此样式采用 CSS 规则的形式。这就是你对粗体的处理:
if (d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
}
要将 CSS class 添加到节点的 classes 列表中,获取节点的 CSS classes 列表getStyleClass()
,并操纵它。
这里你必须小心一点,因为列表可以包含相同值的多个副本,而且你无法控制 updateItem()
被调用的次数以及调用 [=22= 的次数]s 作为参数。最好的选择是删除 class delete-row
的所有实例,并在正确的条件下添加一个:
@Override
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
getStyleClass().removeAll(Collections.singleton("delete-row"));
if (d == null) {
setStyle("");
} else if (d.getAction().equals(Donation.DELETE_DONATION)) {
setStyle("");
getStyleClass().add("delete-row");
} else if (d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
} else {
setStyle("");
}
}
另一种选择是使用 CSS 伪 class 代替:
@Override
public void updateItem(Donation d, boolean empty) {
super.updateItem(d, empty) ;
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete, d != null && d.getAction().equals(Donation.DELETE_DONATION));
if (d != null && d.getAction().equals(Donation.NEW_DONATION)) {
setStyle("-fx-font-weight: bold;");
} else {
setStyle("");
}
}
和
.table-row-cell:delete-row .text {
-fx-strikethrough: true;
}
在这种情况下,为了保持一致性,我可能也会将 NEW_DONATION
样式重构为伪class。
这是一个使用伪classes 的完整示例。请注意,我将 CSS 更改为粗体(据我了解,使用 font-weight
取决于系统为 currently-selected 字体提供粗体字体;使用通用的东西(sans-serif
) 使用 -fx-font
规则更可靠。)
Donation.java
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Donation {
public enum Action { NEW_DONATION, DELETE_DONATION, NO_ACTION }
private final StringProperty name = new SimpleStringProperty() ;
private final ObjectProperty<Action> action = new SimpleObjectProperty<>() ;
public Donation(String name, Action action) {
setName(name);
setAction(action);
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final ObjectProperty<Action> actionProperty() {
return this.action;
}
public final Action getAction() {
return this.actionProperty().get();
}
public final void setAction(final Action action) {
this.actionProperty().set(action);
}
}
App.java
import java.util.Random;
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class App extends Application {
@Override
public void start(Stage stage) {
TableView<Donation> table = new TableView<>();
table.setRowFactory(tv -> {
TableRow<Donation> row = new TableRow<>() {
@Override
protected void updateItem(Donation donation, boolean empty) {
super.updateItem(donation, empty);
PseudoClass add = PseudoClass.getPseudoClass("add-row");
pseudoClassStateChanged(add,
donation != null && donation.getAction() == Donation.Action.NEW_DONATION);
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete,
donation != null && donation.getAction() == Donation.Action.DELETE_DONATION);
}
};
return row ;
});
Random rng = new Random();
for (int i = 1 ; i <= 40 ; i++) {
table.getItems().add(new Donation("Donation "+i, Donation.Action.values()[rng.nextInt(3)]));
}
table.getColumns().add(column("Donation", Donation::nameProperty));
table.getColumns().add(column("Action", Donation::actionProperty));
BorderPane root = new BorderPane(table);
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
private static <S,T> TableColumn<S,T> column(String name, Function<S, Property<T>> prop) {
TableColumn<S,T> col = new TableColumn<>(name);
col.setCellValueFactory(data -> prop.apply(data.getValue()));
return col ;
}
public static void main(String[] args) {
launch();
}
}
style.css:
.table-row-cell:delete-row .text {
-fx-strikethrough: true;
}
.table-row-cell:add-row {
/* -fx-font-weight: bold; */
-fx-font: bold 1em sans-serif ;
}
更新:
如果其中一列未观察到 属性 确定 table 行的样式(例如,在上面的示例中,“action”列不存在),您需要安排行来观察 属性 本身。这有点棘手,因为该行被不同的 table 项重复使用,因此您需要在发生这种情况时从正确的 属性 添加和删除侦听器。这看起来像:
table.setRowFactory(tv -> {
TableRow<Donation> row = new TableRow<>() {
// Listener that updates style when the actionProperty() changes
private final ChangeListener<Donation.Action> listener =
(obs, oldAction, newAction) -> updateStyle();
{
// make sure listener above is registered
// with the correct actionProperty()
itemProperty().addListener((obs, oldDonation, newDonation) -> {
if (oldDonation != null) {
oldDonation.actionProperty().removeListener(listener);
}
if (newDonation != null) {
newDonation.actionProperty().addListener(listener);
}
});
}
@Override
protected void updateItem(Donation donation, boolean empty) {
super.updateItem(donation, empty);
updateStyle();
}
private void updateStyle() {
Donation donation = getItem();
PseudoClass add = PseudoClass.getPseudoClass("add-row");
pseudoClassStateChanged(add, donation != null && donation.getAction() == Donation.Action.NEW_DONATION);
PseudoClass delete = PseudoClass.getPseudoClass("delete-row");
pseudoClassStateChanged(delete, donation != null && donation.getAction() == Donation.Action.DELETE_DONATION);
}
};
return row ;
});