首页 > 解决方案 > 表视图中的行为重复和反向行(javafx)

问题描述

在 tableview 的更新中,我已将代码简化为以下内容。

由于某些原因,我没有像预期的那样使用 JavaFx 的属性,也许这种行为与此有关。来自 JTable 和观察者模式,我想尝试如何在 javafx 中实现这一点。但是,我的表格数据都很好,但是当我尝试更改背景颜色时,表格范围之外会出现更多行。我检查了 Observable 列表的大小,它符合预期。

我无法真正看到或理解 datas.set 是如何产生这种行为的,这是我的代码遗漏的东西,还是 Javafx tableview 的预期行为。

基本上使用的逻辑是我从另一个类(我在这里用线程模拟)接收更新,然后我将用更新的数据更新可观察列表的相应行。

谢谢。

package application;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Pane root = new Pane();
            root.getChildren().add(new TableModel().getTable());
            Scene scene = new Scene(root,400,800);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
package application;

import java.util.Random;

import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.util.Callback;

public class TableModel{

    private TableView<TableData> table = new TableView<>();
    private ObservableList<TableData> datas;

    @SuppressWarnings("unchecked")
    public TableModel() {
        datas = FXCollections.observableArrayList();

        final TableColumn<TableData, Integer> cId = new TableColumn<>("ID");
        cId.setCellValueFactory(
                new Callback<TableColumn.CellDataFeatures<TableData, Integer>, ObservableValue<Integer>>() {
                    @Override
                    public ObservableValue<Integer> call(CellDataFeatures<TableData, Integer> param) {
                        return new SimpleObjectProperty<Integer>(param.getValue().getId());
                    }
                });

        cId.setCellFactory(e -> new TableCell<TableData, Integer>() {
            @Override
            public void updateItem(Integer item, boolean empty) {
                // Always invoke super constructor.
                super.updateItem(item, empty);

                if (item == null || empty) {
                    setText(null);
                } else {
                    setText(String.valueOf(item));
                    if (getIndex() == 0) {
                        this.setStyle("-fx-background-color: green;");
                    }else {
                         this.setStyle("-fx-background-color: red;");
                    }
                }
            }
        });


        table.getColumns().addAll(cId);
        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        table.setItems(datas);
        table.setPrefSize(150, 450);

            datas.add(0, new TableData(0));
            datas.add(1, new TableData(0));
            datas.add(2, new TableData(0));
            datas.add(3, new TableData(0));
            datas.add(4, new TableData(0));


        new Thread(()->{
            while(true) {
                Random rand = new Random(10);
                update(rand.nextInt());
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        }).start(); ;

    }



    public TableView<TableData> getTable() {
        return table;
    }

    public void update(int num) {
        TableData data = new TableData(num);
                Platform.runLater(() -> {
                    datas.set(0, data);
                });
    }

    private class TableData {
        private SimpleIntegerProperty cId = new SimpleIntegerProperty();

        public TableData(Integer conid) {
            this.cId.setValue(conid);
        }

        public Integer getId() {
            return cId.get();
        }

    }
}

输出与datas.set(0, data);

输出错误

输出无datas.set(0, data);

预期产出

标签: javafxtableviewobserver-pattern

解决方案


您的代码有 4 个问题:

第一期

final TableColumn<TableData, Integer> cId = new TableColumn<>("ID");
cId.setCellValueFactory(
        new Callback<TableColumn.CellDataFeatures<TableData, Integer>, ObservableValue<Integer>>() {
            @Override
            public ObservableValue<Integer> call(CellDataFeatures<TableData, Integer> param) {
                return new SimpleObjectProperty<Integer>(param.getValue().getId());
            }
        });

这可以防止属性的更改自动触发更新。例如

table.getItems().get(0).cId.set(5);

不会导致表的更新。要解决此问题,请返回属性本身,而不是在执行时使用属性值初始化的新创建的属性cellValueFactory

final TableColumn<TableData, Number> cId = new TableColumn<>("ID");
cId.setCellValueFactory(
        new Callback<TableColumn.CellDataFeatures<TableData, Number>, ObservableValue<Number>>() {

            @Override
            public ObservableValue<Number> call(TableColumn.CellDataFeatures<TableData, Number> param) {
                return param.getValue().cId;
            }
        });

第 2 期

@Override
public void updateItem(Integer item, boolean empty) {
    // Always invoke super constructor.
    super.updateItem(item, empty);

    if (item == null || empty) {
        setText(null);
    } else {
        setText(String.valueOf(item));
        if (getIndex() == 0) {
            this.setStyle("-fx-background-color: green;");
        }else {
             this.setStyle("-fx-background-color: red;");
        }
    }
}

考虑到单元格在填充了值后可能会变为空,因此此实现是不正确的。您应该确保在单元格变空的情况下恢复“空”外观。这需要您清除样式属性:

@Override
protected void updateItem(Number item, boolean empty) {
    // Always invoke supertype's implementation
    super.updateItem(item, empty);

    if (item == null || empty) {
        setText(null);
        setStyle(null);
    } else {
        setText(item.toString());
        if (getIndex() == 0) {
            this.setStyle("-fx-background-color: green;");
        } else {
            this.setStyle("-fx-background-color: red;");
        }
    }
}

问题 3 和 4

new Thread(()->{
    while(true) {
        Random rand = new Random(10);
        update(rand.nextInt());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }
}).start();

即使关闭 GUI 以防止程序终止,该线程仍将保持活动状态。您可以使用守护线程轻松解决此问题。

此外Random,用种子初始化会返回确定性结果。由于您Random在循环的每次迭代中都使用相同的种子重新初始化,因此rand.nextInt()返回的值始终相同。您需要移动Random循环外部的初始化。

Thread thread = new Thread(() -> {
    Random rand = new Random(10);
    while (true) {
        update(rand.nextInt());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }
});
thread.setDaemon(true);
thread.start();

推荐阅读