【问题标题】:Is it necessary to create classes for every single database query in order to work with TableView?是否需要为每个数据库查询创建类才能使用 TableView?
【发布时间】:2021-08-07 17:35:51
【问题描述】:

在过去的几天里,我浏览了一些解释“如何使用 TableViews?”的 JavaFX 教程。每个教程都会为example 创建像Person 这样的DataModel-Objects。这很好,只要您只需要像 select Firstname, Lastname from Person 这样的 sql 语句。

但是,如果您不想为每个选择查询创建数据模型,因为您出于某种原因将数据与另一个对象连接起来怎么办?例如,select * from Person join City join Country。我最近几天了解到的是,如果您创建一个仅包含 List(代表行)的 tableview,那么处理格式化甚至评估 tableview 会变得很糟糕。

这真的是 javaFX 的东西吗?还是我错过了什么?

更新

为了更清楚,请看这个question。在阅读了我的问题的答案和 cmets 后,我首先发现了这个问题,但我对链接问题的答案并不满意。

【问题讨论】:

  • 您有正在谈论的特定用例吗?应用程序中的 UI 控件通常会显示特定查询的结果。设计一个 UI 控件来显示任意查询的结果并不常见。
  • 也许这是一个副本:Create a dynamic tableview with generic types

标签: java sql oop javafx tableview


【解决方案1】:

您的问题在这里混淆了两个概念:一方面是javafx.scene.control.TableView,另一方面是 SQL/ORM。让我们忘记 SQL 查询,我认为您的关注点主要在 TableView

TableView 必须填充List。此列表的内部类型必须与 TableView 的泛型类型匹配。因此,例如TableView<Person> 将填充List<Person>

除此之外,表示行的对象的实际类型可以是anything。它不必 甚至必须包含数据本身,或者它很可能是,例如,Map<String, Object>。在这种情况下,您将通过为每一列定义一个CellValueFactory 将行的键映射到每一列,这将返回您选择的键的条目值。

然后,您可以通过为列定义 CellFactory 来将此值转换为 String 和/或 Node

可以更轻松地映射具有 JavaFX 属性的对象,因为存在只需要属性名称的预制 PropertyValueFactory。但它们并不是唯一的出路。

对每一行使用Map<String, Object> 的示例:

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class App extends Application {

  private TableView<Map<String, Object>> tableView;
  private TableColumn<Map<String, Object>, String> nameCol;
  private TableColumn<Map<String, Object>, Integer> ageCol;
  
  private Stage stage;

  @Override
  public void start(Stage stage) {
    this.stage = stage;

    //Insert a TableView into scene
    this.tableView = new TableView<>();
    this.nameCol = new TableColumn<>("Name");
    this.nameCol.setPrefWidth(250);
    ageCol = new TableColumn<>("Age");
    tableView.getColumns().add(nameCol);
    tableView.getColumns().add(ageCol);
    
    nameCol.setCellValueFactory(param -> {
      Map<String, Object> v = param.getValue();
      return new SimpleStringProperty(v == null ? null : (String) v.get("name"));
    });
    
    ageCol.setCellValueFactory(param -> {
      Map<String, Object> v = param.getValue();
      return new SimpleObjectProperty<Integer>(v == null ? null : (Integer) v.get("age"));
    });
    ageCol.setCellFactory(param -> {
      return new TableCell<>() {
        @Override
        public void updateItem(Integer item, boolean empty) {
          if (empty || item == null)
            super.setText("");
          else
            super.setText(NumberFormat.getIntegerInstance().format(item));
          //Could also call setGraphic(Node)
        }
      };
    });
    
    final Scene scene = new Scene(new BorderPane(tableView), 640, 480);

    stage.setScene(scene);
    stage.setOnShown(event -> stageReady()); //Continue when stage is rendered
    stage.show();
  }

  private void stageReady() {
    //Generate data
    List<Map<String, Object>> data = new ArrayList<>();
    Map<String, Object> obj1 = new HashMap<>();
    obj1.put("name", "Name of Object 1");
    obj1.put("age", 42);
    data.add(obj1);
    Map<String, Object> obj2 = new HashMap<>();
    obj2.put("name", "OBJECT 2");
    obj2.put("age", 53);
    data.add(obj2);
    //Show data
    tableView.getItems().setAll(data);
  }

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

}

ageCol.setCellFactory 仅用于演示。如果我没有设置它,那么 Integer 单元格将被渲染为单元格 Integer.toString() 调用 text 属性。

【讨论】:

  • 是的,正确。 SQL 不是问题......也许我的例子没有很好地选择。那么最常见的方法是使用集合(例如地图)来处理这种混合实体?我还能使用地图绑定吗?例如,我想根据其单元格内容设置表格视图的样式
  • 集合,来自 json-simple 的 JsonObjects,任何东西
  • 您可以使用绑定,但如果没有 JavaFX 属性,它们将不会对数据对象中的更改做出反应。您还可以使用CellFactory 生成的TableCell 在其updateItem 方法中更新样式
  • 您的TableView 也有一个行工厂,其工作方式与单元工厂类似。我可以整天坚持下去。如果您需要更多指导,请提出具体问题。
【解决方案2】:

here 所示,数据库查询可用于构造属性值对的ObservableList&lt;Map&gt;。如Adding Maps of Data to the Table 所示,MapValueFactory 是“Callback 接口的便捷实现”,如PropertyValueFactory,“专为在TableColumn 内使用而设计。

受此相关example 的启发,下面的变体显示了一个TableView,其中包含System.getenv() 返回的Map&lt;String,​String&gt; 中包含的条目。尽管该地图不可修改,但显示了简单的编辑支持。

import java.util.Map;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

/**
 * https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/table-view.htm
 * https://stackoverflow.com/a/18620705/230513
 * https://stackoverflow.com/a/69781121/230513
 * https://stackoverflow.com/a/9134371/230513
 */
public class TableMapSample extends Application {

    private final String columnKey = "Key";
    private final String columnValue = "Value";

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

    @Override
    public void start(Stage stage) {
        ObservableList<Map.Entry<String, String>> list
            = FXCollections.observableArrayList(System.getenv().entrySet());
        TableView<Map.Entry<String, String>> table = new TableView<>(list);
        table.setEditable(true);
        table.getSelectionModel().setCellSelectionEnabled(true);

        TableColumn<Map.Entry<String, String>, String> keyColumn = new TableColumn<>(columnKey);
        keyColumn.setCellValueFactory((TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p)
            -> new SimpleStringProperty(p.getValue().getKey()));
        table.getColumns().add(keyColumn);
        keyColumn.setCellFactory(TextFieldTableCell.forTableColumn());

        TableColumn<Map.Entry<String, String>, String> valueColumn = new TableColumn<>(columnValue);
        valueColumn.setCellValueFactory((TableColumn.CellDataFeatures<Map.Entry<String, String>, String> p)
            -> new SimpleStringProperty(p.getValue().getValue()));
        valueColumn.setCellFactory(TextFieldTableCell.forTableColumn());
        valueColumn.setPrefWidth(800);
        table.getColumns().add(valueColumn);

        stage.setTitle("Table Map.Entry<K,V>");
        stage.setScene(new Scene(new StackPane(table)));
        stage.show();
    }
}

【讨论】:

    猜你喜欢
    • 2020-12-11
    • 2013-06-17
    • 1970-01-01
    • 2021-09-19
    • 2017-03-13
    • 1970-01-01
    • 2013-07-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多