首页 > 解决方案 > JavaFX TableView 上下文菜单获取选定的 ID

问题描述

我有一个 TableView,我在其中显示数据库中的数据。TableView 本身并没有硬编码在特定的表上,而是添加了列和数据。我添加了上下文菜单,所以当用户右键单击一个项目时,它会删除它。它工作正常,但我不知道如何获取第一列的 ID。请注意,我不想要选定的索引,而是 tableView (第一列)中的 ID。

被质疑的片段:

removeMenuItem.setOnAction(event -> {
    System.out.println(row.getItem()); // HERE

    this.tableView.getItems().remove(row.getItem());
});

基本上,row.getItem()返回记录:

[2, Name, Stuff, Test Test, Category]

问题是如何获得 ID(在本例中为 2)或名称?

完整代码:

package controllers;

import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.util.Callback;

import java.sql.ResultSet;
import java.sql.SQLException;

public class TableController {

    @FXML
    private Label descriptionLabel;

    @FXML
    private TableView tableView;

    public void setTableResultSet(String label, ResultSet resultSet) throws SQLException {
        // Create context menu
        createContextMenu();

        // Set label text
        descriptionLabel.setText(label);

        // Resolve columns
        ObservableList<ObservableList> data = resolveTableColumns(resultSet);

        // Add records
        addTableRecords(resultSet, data);
    }

    /**
     * Create a context menu responsible for removing items.
     */
    private void createContextMenu() {
        tableView.setRowFactory(tableView -> {
            final TableRow row = new TableRow();
            final ContextMenu contextMenu = new ContextMenu();
            final MenuItem removeMenuItem = new MenuItem("Remove");
            removeMenuItem.setOnAction(event -> {
                System.out.println(row.getItem()); // HERE

                this.tableView.getItems().remove(row.getItem());
            });
            contextMenu.getItems().add(removeMenuItem);

            // Set context menu on row, but use a binding to make it only show for non-empty rows:
            row.contextMenuProperty().bind(
                    Bindings.when(row.emptyProperty())
                            .then((ContextMenu) null)
                            .otherwise(contextMenu)
            );

            return row;
        });
    }

    /**
     * Resolves table columns based on a ResultSet.
     *
     * @param resultSet the ResultSet.
     * @return
     * @throws SQLException
     */
    private ObservableList<ObservableList> resolveTableColumns(ResultSet resultSet) throws SQLException {
        ObservableList<ObservableList> data = FXCollections.observableArrayList();
        for (int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) {
            final int j = i;
            TableColumn col = new TableColumn(resultSet.getMetaData().getColumnName(i + 1));
            col.setCellValueFactory((Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>) param -> {
                return new SimpleStringProperty(param.getValue().get(j).toString());
            });
            tableView.getColumns().add(col);
        }
        return data;
    }

    /**
     * Adds table data from a ResultSet.
     *
     * @param resultSet the ResultSet.
     * @param data      the data.
     * @throws SQLException
     */
    private void addTableRecords(ResultSet resultSet, ObservableList<ObservableList> data) throws SQLException {
        while (resultSet.next()) {
            ObservableList<String> row = FXCollections.observableArrayList();
            for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
                row.add(resultSet.getString(i));
            }
            data.add(row);
        }
        tableView.setItems(data);
    }
}

根据@James_D 的评论进行编辑:

package controllers;

import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.util.Callback;

import java.sql.ResultSet;
import java.sql.SQLException;

public class TableController {

    @FXML
    private Label descriptionLabel;

    @FXML
    private TableView<ObservableList<String>> tableView;

    public void setTableResultSet(String label, ResultSet resultSet) throws SQLException {
        // Create context menu
        createContextMenu();

        // Set label text
        descriptionLabel.setText(label);

        // Resolve columns
        ObservableList<ObservableList<String>> data = resolveTableColumns(resultSet);

        // Add records
        addTableRecords(resultSet, data);
    }

    /**
     * Create a context menu responsible for removing items.
     */
    private void createContextMenu() {
        tableView.setRowFactory(tableView -> {
            final TableRow<ObservableList<String>> row = new TableRow<>();
            final ContextMenu contextMenu = new ContextMenu();
            final MenuItem removeMenuItem = new MenuItem("Изтрий");
            removeMenuItem.setOnAction(event -> {
                System.out.println(row.getItem().get(1));

                this.tableView.getItems().remove(row.getItem());
            });
            contextMenu.getItems().add(removeMenuItem);

            // Set context menu on row, but use a binding to make it only show for non-empty rows:
            row.contextMenuProperty().bind(
                    Bindings.when(row.emptyProperty())
                            .then((ContextMenu) null)
                            .otherwise(contextMenu)
            );

            return row;
        });
    }

    /**
     * Resolves table columns based on a ResultSet.
     *
     * @param resultSet the ResultSet.
     * @return
     * @throws SQLException
     */
    private ObservableList<ObservableList<String>> resolveTableColumns(ResultSet resultSet) throws SQLException {
        ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
        for (int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) {
            final int j = i;
            TableColumn col = new TableColumn(resultSet.getMetaData().getColumnName(i + 1));
            col.setCellValueFactory((Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>) param -> {
                return new SimpleStringProperty(param.getValue().get(j).toString());
            });
            tableView.getColumns().add(col);
        }
        return data;
    }

    /**
     * Adds table data from a ResultSet.
     *
     * @param resultSet the ResultSet.
     * @param data      the data.
     * @throws SQLException
     */
    private void addTableRecords(ResultSet resultSet, ObservableList<ObservableList<String>> data) throws SQLException {
        while (resultSet.next()) {
            ObservableList<String> row = FXCollections.observableArrayList();
            for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
                row.add(resultSet.getString(i));
            }
            data.add(row);
        }
        tableView.setItems(data);
    }
}

标签: javafx

解决方案


不要使用原始类型:这样您就可以访问从表中检索到的数据的适当方法(无需在任何地方向下转换)。

具体来说,您声明您将数据表示为 ObservableList<ObservableList>,这意味着每一行都表示为 (raw) ObservableList。因为,看看你的addTableRecords方法,一行的每个元素都是 a String,所以行应该表示为ObservableList<String>,而整体数据表示为ObservableList<ObservableList<String>>

和相关的TableViewUI 元素也应该被参数化:即你应该使用

  • TableView<ObservableList<String>>
  • TableColumn<ObservableList<String>, String>
  • TableRow<ObservableList<String>

如果您进行这些更改,那么在您的createContextMenu()方法中,您将拥有

TableRow<ObservableList<String>> row = new TableRow<>(); 

因此,返回类型row.getItem()将是,您可以在返回值上ObservableList<String>调用ObservableList(或更一般地说, )中定义的方法。List具体来说,调用get(...)String在行中的给定单元格中返回 :

String id = row.getItem().get(0);
String name = row.getItem().get(1);

等等

放在一起,这看起来像

package controllers;

import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.util.Callback;

import java.sql.ResultSet;
import java.sql.SQLException;

public class TableController {

    @FXML
    private Label descriptionLabel;

    @FXML
    private TableView<ObservableList<String>> tableView;

    public void setTableResultSet(String label, ResultSet resultSet) throws SQLException {
        // Create context menu
        createContextMenu();

        // Set label text
        descriptionLabel.setText(label);

        // Resolve columns
        ObservableList<ObservableList<String>> data = resolveTableColumns(resultSet);

        // Add records
        addTableRecords(resultSet, data);
    }

    /**
     * Create a context menu responsible for removing items.
     */
    private void createContextMenu() {
        tableView.setRowFactory(tableView -> {
            final TableRow<ObservableList<String>> row = new TableRow<>();
            final ContextMenu contextMenu = new ContextMenu();
            final MenuItem removeMenuItem = new MenuItem("Изтрий");
            removeMenuItem.setOnAction(event -> {
                String id = row.getItem().get(0);
                String name = row.getItem().get(1);
                // do whatever you need with id and name, etc.

                this.tableView.getItems().remove(row.getItem());
            });
            contextMenu.getItems().add(removeMenuItem);

            // Set context menu on row, but use a binding to make it only show for non-empty rows:
            row.contextMenuProperty().bind(
                    Bindings.when(row.emptyProperty())
                            .then((ContextMenu) null)
                            .otherwise(contextMenu)
            );

            return row;
        });
    }

    /**
     * Resolves table columns based on a ResultSet.
     *
     * @param resultSet the ResultSet.
     * @return
     * @throws SQLException
     */
    private ObservableList<ObservableList<String>> resolveTableColumns(ResultSet resultSet) throws SQLException {
        ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
        for (int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) {
            final int j = i;
            TableColumn<ObservableList<String>, String> col = new TableColumn<>(resultSet.getMetaData().getColumnName(i + 1));
            col.setCellValueFactory(param -> 
                new SimpleStringProperty(param.getValue().get(j))
            );
            tableView.getColumns().add(col);
        }
        return data;
    }

    /**
     * Adds table data from a ResultSet.
     *
     * @param resultSet the ResultSet.
     * @param data      the data.
     * @throws SQLException
     */
    private void addTableRecords(ResultSet resultSet, ObservableList<ObservableList<String>> data) throws SQLException {
        while (resultSet.next()) {
            ObservableList<String> row = FXCollections.observableArrayList();
            for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
                row.add(resultSet.getString(i));
            }
            data.add(row);
        }
        tableView.setItems(data);
    }
}

推荐阅读