根据用于填充 table 的列表元素中未显示的 java bean 值设置 JavaFX FXML table 行文本颜色
Set JavaFX FXML table row text color based on an undisplayed java bean value in the List Element used to populate the table
我正在尝试将我在 swing 中构建的应用程序转换为 JavaFx。这包括彻底修改 GUI 设计。应用程序按如下方式处理数据:
从数据库中检索数据。数据库中的每一行都被解析为一个 java bean,每个 bean 都被添加到一个 ArrayList 中。然后将数组列表返回给调用方法,然后解析为 ObservableList 以使其与 JavaFX table 视图兼容。然后,我通过向其中添加每个 List 元素(这是一个 java bean)来填充 table。
重要的是,组成 table 每一行的 Java bean 有 12 个元素。 table 在其 9 列中仅向用户显示其中的 9 列。我想要做的是采用行列表元素中其他未显示的值之一,并使用它来确定显示行的文本颜色是设置为红色还是绿色。我似乎无法管理这个。我在 Stack 和其他论坛上查看了其他几个类似的问题,它们似乎解决了为特定单元格或列而不是行设置单元格文本颜色的问题。他们似乎也依靠可见的显示值来做到这一点。我尝试了几种方法,但其中 none 似乎有效,而且看起来很复杂。必须有一个更直接的方法来处理我正在尝试做的事情,因为这必须是一个相当普遍的要求。有人可以告诉我怎么做吗?
我的table在FXML中定义如下:
<TableView fx:id="toDoTable" editable="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="todoID" prefWidth="59.0" text="ID" />
<TableColumn id="columnHeader" fx:id="Charity" prefWidth="77.0" text="Charity" />
<TableColumn fx:id="todoFunder" prefWidth="101.0" text="Funder" />
<TableColumn fx:id="todoType" prefWidth="92.0" text="Task Type" />
<TableColumn fx:id="todoInternalDeadline" prefWidth="145.0" text="Internal Deadline" />
<TableColumn fx:id="todoExternalDeadline" prefWidth="145.0" text="External Deadline" />
<TableColumn fx:id="todoHrs" prefWidth="140.0" text="Target Hours" />
<TableColumn fx:id="todoActualHrs" prefWidth="110.0" text="Actual Hours" />
<TableColumn fx:id="todoDescription" prefWidth="110.0" text="Description" />
</columns>
</TableView>
table在对应controllerclass中的初始化方法中填充如下:
public void initialize(URL url, ResourceBundle rb) {
todoID.setCellValueFactory(new PropertyValueFactory<>("taskID"));
todoClient.setCellValueFactory(new PropertyValueFactory<>("Charity"));
todoFunder.setCellValueFactory(new PropertyValueFactory<>("taskFunder"));
todoType.setCellValueFactory((new PropertyValueFactory<>("taskType")));
todoInternalDeadline.setCellValueFactory((new PropertyValueFactory<>("internalDeadline")));
todoExternalDeadline.setCellValueFactory((new PropertyValueFactory<>("externalDeadline")));
todoHrs.setCellValueFactory((new PropertyValueFactory<>("assignedHours")));
todoActualHrs.setCellValueFactory((new PropertyValueFactory<>("hoursCompleted")));
todoDescription.setCellValueFactory((new PropertyValueFactory<>("taskDescription")));
ObservableList<Task> list = FXCollections.observableArrayList(parseTaskBeans());//parseTaskBeans();
toDoTable.getItems().addAll(list);
GuiUtility.autoResizeColumns(toDoTable);
//toDoTable.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
toDoTable.autosize();
}
虽然以上将填充整个 table,但我认为我需要做的是单独处理行并在将它们添加到视图之前为它们着色。我假设颜色必须是 table 元素的 属性 而不是列表,因为列表只是数据。我最近的尝试在下面,但我认为它一定是完全错误的,因为我无法找到一种方法来获取行中的文本来定义其颜色。所以我在我认为需要添加代码来解决这个问题的地方留下了评论:
for(int i = 0; i<list.size(); i++){
System.out.println(list.get(i).getTaskReturned());
if(list.get(i).getTaskReturned().equalsIgnoreCase("false")){
//set Color red
}
else{
//set color green
}
toDoTable.getItems().add(list.get(i));
}
我的另一个想法是使用 lambda 来处理 table 行内容,但我又可以看到我是如何到达实际行的。逐个单元地设置每个元素似乎真的很复杂,所以必须有一种完全不同的方式来考虑这个,我没有得到。如果有人可以解释并告诉我如何做到这一点,我将不胜感激。
代码示例,希望对您的案例有所帮助:
public class Main extends Application {
private TableView table = new TableView();
@Override
public void start(Stage primaryStage) throws Exception{
ObservableList<Data> data = FXCollections.observableArrayList(
new Data("Jacob", "Smith", true),
new Data("Isabella", "Johnson",true),
new Data("Ethan", "Williams", false),
new Data("Emma", "Jones", true),
new Data("Michael", "Brown", true)
);
TableColumn firstDataCol = new TableColumn("Data1");
firstDataCol.setMinWidth(100);
firstDataCol.setCellValueFactory(
new PropertyValueFactory<Data, String>("data1"));
TableColumn secondDataCol = new TableColumn("Data2");
secondDataCol.setMinWidth(100);
secondDataCol.setCellValueFactory(
new PropertyValueFactory<Data, String>("data2"));
/*TableColumn isGreenCol = new TableColumn("IsGreen");
isGreenCol.setMinWidth(200);
isGreenCol.setCellValueFactory(
new PropertyValueFactory<Data, Boolean>("isGreen"));*/
table.setRowFactory(new Callback<TableView<Data>, TableRow<Data>>() {
@Override
public TableRow<Data> call(TableView<Data> tableView) {
final TableRow<Data> row = new TableRow<Data>() {
@Override
protected void updateItem(Data data, boolean empty){
super.updateItem(data, empty);
if (data!=null&&data.isGreen.get()) {
setStyle("-fx-text-background-color: green;");
} else {
setStyle("-fx-text-background-color: red;");
}
}
};
return row;
}
});
table.setItems(data);
table.getColumns().addAll(firstDataCol, secondDataCol);
Parent window = new VBox();
((VBox) window).getChildren().add(new Label("example of small window:"));
primaryStage.setTitle("example");
((VBox) window).getChildren().add(table);
Scene scene=new Scene(window);
primaryStage.setScene(scene);
primaryStage.show();
}
public class Data {
private final SimpleStringProperty data1;
private final SimpleStringProperty data2;
private final SimpleBooleanProperty isGreen;
public Data(String data1, String data2, Boolean isGreen) {
this.data1 = new SimpleStringProperty(data1);
this.data2 = new SimpleStringProperty(data2);
this.isGreen = new SimpleBooleanProperty(isGreen);
}
public String getData1() {
return data1.get();
}
public SimpleStringProperty data1Property() {
return data1;
}
public String getData2() {
return data2.get();
}
public SimpleStringProperty data2Property() {
return data2;
}
public boolean isIsGreen() {
return isGreen.get();
}
public SimpleBooleanProperty isGreenProperty() {
return isGreen;
}
}
public static void main(String[] args) {
launch(args);
}
}
输出画面:
Java 文档的解释:
setRowFactory:
A function which produces a TableRow. The system is responsible for
reusing TableRows. Return from this function a TableRow which might be
usable for representing a single row in a TableView.
Note that a
TableRow is not a TableCell. A TableRow is simply a container
for a TableCell, and in most circumstances it is more likely that
you'll want to create custom TableCells, rather than TableRows. The
primary use case for creating custom TableRow instances would most
probably be to introduce some form of column spanning support.
You
can create custom TableCell instances per column by assigning the
appropriate function to the cellFactory property in the TableColumn
class. @return rowFactory property
并且对一行中的每个单元格调用 updateItem:
updateItem:
The updateItem method should not be called by developers, but it is
the best method for developers to override to allow for them to
customise the visuals of the cell. To clarify, developers should never
call this method in their code (they should leave it up to the UI
control, such as the ListView control) to call this method. However,
the purpose of having the updateItem method is so that developers,
when specifying custom cell factories (again, like the ListView cell
factory), the updateItem method can be overridden to allow for
complete customisation of the cell. It is very important that
subclasses of Cell override the updateItem method properly, as failure
to do so will lead to issues such as blank cells or cells with
unexpected content appearing within them. Here is an example of how to
properly override the updateItem method:
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
}
}
Note in this code sample two important points: We call the super.updateItem(T, boolean) method. If this is not done, the item and
empty properties are not correctly set, and you are likely to end up
with graphical issues. We test for the empty condition, and if true,
we set the text and graphic properties to null. If we do not do this,
it is almost guaranteed that end users will see graphical artifacts in
cells unexpectedly. Overrides: updateItem in class Cell Params: data –
The new item for the cell. empty – whether or not this cell represents
data from the list. If it is empty, then it does not represent any
domain data, but is a cell being used to render an "empty" row.
我正在尝试将我在 swing 中构建的应用程序转换为 JavaFx。这包括彻底修改 GUI 设计。应用程序按如下方式处理数据:
从数据库中检索数据。数据库中的每一行都被解析为一个 java bean,每个 bean 都被添加到一个 ArrayList 中。然后将数组列表返回给调用方法,然后解析为 ObservableList 以使其与 JavaFX table 视图兼容。然后,我通过向其中添加每个 List 元素(这是一个 java bean)来填充 table。
重要的是,组成 table 每一行的 Java bean 有 12 个元素。 table 在其 9 列中仅向用户显示其中的 9 列。我想要做的是采用行列表元素中其他未显示的值之一,并使用它来确定显示行的文本颜色是设置为红色还是绿色。我似乎无法管理这个。我在 Stack 和其他论坛上查看了其他几个类似的问题,它们似乎解决了为特定单元格或列而不是行设置单元格文本颜色的问题。他们似乎也依靠可见的显示值来做到这一点。我尝试了几种方法,但其中 none 似乎有效,而且看起来很复杂。必须有一个更直接的方法来处理我正在尝试做的事情,因为这必须是一个相当普遍的要求。有人可以告诉我怎么做吗?
我的table在FXML中定义如下:
<TableView fx:id="toDoTable" editable="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<columns>
<TableColumn fx:id="todoID" prefWidth="59.0" text="ID" />
<TableColumn id="columnHeader" fx:id="Charity" prefWidth="77.0" text="Charity" />
<TableColumn fx:id="todoFunder" prefWidth="101.0" text="Funder" />
<TableColumn fx:id="todoType" prefWidth="92.0" text="Task Type" />
<TableColumn fx:id="todoInternalDeadline" prefWidth="145.0" text="Internal Deadline" />
<TableColumn fx:id="todoExternalDeadline" prefWidth="145.0" text="External Deadline" />
<TableColumn fx:id="todoHrs" prefWidth="140.0" text="Target Hours" />
<TableColumn fx:id="todoActualHrs" prefWidth="110.0" text="Actual Hours" />
<TableColumn fx:id="todoDescription" prefWidth="110.0" text="Description" />
</columns>
</TableView>
table在对应controllerclass中的初始化方法中填充如下:
public void initialize(URL url, ResourceBundle rb) {
todoID.setCellValueFactory(new PropertyValueFactory<>("taskID"));
todoClient.setCellValueFactory(new PropertyValueFactory<>("Charity"));
todoFunder.setCellValueFactory(new PropertyValueFactory<>("taskFunder"));
todoType.setCellValueFactory((new PropertyValueFactory<>("taskType")));
todoInternalDeadline.setCellValueFactory((new PropertyValueFactory<>("internalDeadline")));
todoExternalDeadline.setCellValueFactory((new PropertyValueFactory<>("externalDeadline")));
todoHrs.setCellValueFactory((new PropertyValueFactory<>("assignedHours")));
todoActualHrs.setCellValueFactory((new PropertyValueFactory<>("hoursCompleted")));
todoDescription.setCellValueFactory((new PropertyValueFactory<>("taskDescription")));
ObservableList<Task> list = FXCollections.observableArrayList(parseTaskBeans());//parseTaskBeans();
toDoTable.getItems().addAll(list);
GuiUtility.autoResizeColumns(toDoTable);
//toDoTable.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
toDoTable.autosize();
}
虽然以上将填充整个 table,但我认为我需要做的是单独处理行并在将它们添加到视图之前为它们着色。我假设颜色必须是 table 元素的 属性 而不是列表,因为列表只是数据。我最近的尝试在下面,但我认为它一定是完全错误的,因为我无法找到一种方法来获取行中的文本来定义其颜色。所以我在我认为需要添加代码来解决这个问题的地方留下了评论:
for(int i = 0; i<list.size(); i++){
System.out.println(list.get(i).getTaskReturned());
if(list.get(i).getTaskReturned().equalsIgnoreCase("false")){
//set Color red
}
else{
//set color green
}
toDoTable.getItems().add(list.get(i));
}
我的另一个想法是使用 lambda 来处理 table 行内容,但我又可以看到我是如何到达实际行的。逐个单元地设置每个元素似乎真的很复杂,所以必须有一种完全不同的方式来考虑这个,我没有得到。如果有人可以解释并告诉我如何做到这一点,我将不胜感激。
代码示例,希望对您的案例有所帮助:
public class Main extends Application {
private TableView table = new TableView();
@Override
public void start(Stage primaryStage) throws Exception{
ObservableList<Data> data = FXCollections.observableArrayList(
new Data("Jacob", "Smith", true),
new Data("Isabella", "Johnson",true),
new Data("Ethan", "Williams", false),
new Data("Emma", "Jones", true),
new Data("Michael", "Brown", true)
);
TableColumn firstDataCol = new TableColumn("Data1");
firstDataCol.setMinWidth(100);
firstDataCol.setCellValueFactory(
new PropertyValueFactory<Data, String>("data1"));
TableColumn secondDataCol = new TableColumn("Data2");
secondDataCol.setMinWidth(100);
secondDataCol.setCellValueFactory(
new PropertyValueFactory<Data, String>("data2"));
/*TableColumn isGreenCol = new TableColumn("IsGreen");
isGreenCol.setMinWidth(200);
isGreenCol.setCellValueFactory(
new PropertyValueFactory<Data, Boolean>("isGreen"));*/
table.setRowFactory(new Callback<TableView<Data>, TableRow<Data>>() {
@Override
public TableRow<Data> call(TableView<Data> tableView) {
final TableRow<Data> row = new TableRow<Data>() {
@Override
protected void updateItem(Data data, boolean empty){
super.updateItem(data, empty);
if (data!=null&&data.isGreen.get()) {
setStyle("-fx-text-background-color: green;");
} else {
setStyle("-fx-text-background-color: red;");
}
}
};
return row;
}
});
table.setItems(data);
table.getColumns().addAll(firstDataCol, secondDataCol);
Parent window = new VBox();
((VBox) window).getChildren().add(new Label("example of small window:"));
primaryStage.setTitle("example");
((VBox) window).getChildren().add(table);
Scene scene=new Scene(window);
primaryStage.setScene(scene);
primaryStage.show();
}
public class Data {
private final SimpleStringProperty data1;
private final SimpleStringProperty data2;
private final SimpleBooleanProperty isGreen;
public Data(String data1, String data2, Boolean isGreen) {
this.data1 = new SimpleStringProperty(data1);
this.data2 = new SimpleStringProperty(data2);
this.isGreen = new SimpleBooleanProperty(isGreen);
}
public String getData1() {
return data1.get();
}
public SimpleStringProperty data1Property() {
return data1;
}
public String getData2() {
return data2.get();
}
public SimpleStringProperty data2Property() {
return data2;
}
public boolean isIsGreen() {
return isGreen.get();
}
public SimpleBooleanProperty isGreenProperty() {
return isGreen;
}
}
public static void main(String[] args) {
launch(args);
}
}
输出画面:
Java 文档的解释:
setRowFactory:
A function which produces a TableRow. The system is responsible for reusing TableRows. Return from this function a TableRow which might be usable for representing a single row in a TableView.
Note that a TableRow is not a TableCell. A TableRow is simply a container for a TableCell, and in most circumstances it is more likely that you'll want to create custom TableCells, rather than TableRows. The primary use case for creating custom TableRow instances would most probably be to introduce some form of column spanning support.
You can create custom TableCell instances per column by assigning the appropriate function to the cellFactory property in the TableColumn class. @return rowFactory property
并且对一行中的每个单元格调用 updateItem:
updateItem:
The updateItem method should not be called by developers, but it is the best method for developers to override to allow for them to customise the visuals of the cell. To clarify, developers should never call this method in their code (they should leave it up to the UI control, such as the ListView control) to call this method. However, the purpose of having the updateItem method is so that developers, when specifying custom cell factories (again, like the ListView cell factory), the updateItem method can be overridden to allow for complete customisation of the cell. It is very important that subclasses of Cell override the updateItem method properly, as failure to do so will lead to issues such as blank cells or cells with unexpected content appearing within them. Here is an example of how to properly override the updateItem method:
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
}
}
Note in this code sample two important points: We call the super.updateItem(T, boolean) method. If this is not done, the item and empty properties are not correctly set, and you are likely to end up with graphical issues. We test for the empty condition, and if true, we set the text and graphic properties to null. If we do not do this, it is almost guaranteed that end users will see graphical artifacts in cells unexpectedly. Overrides: updateItem in class Cell Params: data – The new item for the cell. empty – whether or not this cell represents data from the list. If it is empty, then it does not represent any domain data, but is a cell being used to render an "empty" row.