I am using tableview in javafx and the first column is taking a lot of space. Sometimes tableview columns works normally and sometimes it doesn't. I am dynamically making columns and adding to tableview. I am using constrained-resize policy.
JDK 1.8 is used. JavaFx 8 is used. The UI is FXML based.
Below is the example code that can be used to generate the problem. Please note that sometimes the column width are as expected and sometime first column takes a lot of space. Same behaviour is not everytime with the same code. The minimum 3 files needed with minimum code for fxml based app is
Main Class
public class StackOverFlowQuestion extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
Screen screen = Screen.getPrimary();
Rectangle2D bounds = screen.getVisualBounds();
stage.setWidth(bounds.getWidth());
stage.setHeight(bounds.getHeight());
stage.setMaximized(true);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Controller Class
public class FXMLDocumentController implements Initializable {
#FXML
private TableView tableview;
private ObservableList<ObservableList<String>> data;
#Override
public void initialize(URL url, ResourceBundle rb) {
buildData();
}
public void buildData() {
data = FXCollections.observableArrayList();
/**
* ********************************
* TABLE COLUMN ADDED DYNAMICALLY * ********************************
*/
for (int i = 0; i < 11; i++) {
//We are using non property style for making dynamic table
final int j = i;
TableColumn col = new TableColumn("Column No." + i);
col.setCellValueFactory(new Callback<CellDataFeatures<ObservableList, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<ObservableList, String> param) {
return new SimpleStringProperty(param.getValue().get(j).toString());
}
});
tableview.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
tableview.getColumns().addAll(col);
}
/**
* ******************************
* Data added to ObservableList * ******************************
*/
for (int j = 0; j < 10; j++) {
//Iterate Row
ObservableList<String> row = FXCollections.observableArrayList();
for (int i = 0; i < 11; i++) {
//Iterate Column
row.add("Long Data to exceed width " + j + " " + i);
}
data.add(row);
}
tableview.setItems(data);
//FINALLY ADDED TO TableView
}
}
FXML Document
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" prefHeight="700.0" prefWidth="1300.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.171" fx:controller="stackoverflowquestion.FXMLDocumentController">
<children>
<VBox layoutX="341.0" layoutY="27.0" prefHeight="538.0" prefWidth="1300.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="10.0">
<children>
<TableView fx:id="tableview" prefHeight="200.0" />
</children>
</VBox>
</children>
</AnchorPane>
Below is the actual image from project.
Can somebody help please?
Related
I have two controllers:
Main which contains TableView Tab which displays SQLITE database tables (which contain different number of columns) in tabular format, and
Rem which contains a button which dictates which database to display (there are many such controllers).
Now if I create a method in Main which is responsible for updating and displaying TableView contents, how do I use that method in other controller?
I created two static ObservableList variables columns and rows and bind them to Tab in Main's initialize.
APP.java
public class App extends Application {
private static Scene scene;
public static Connection conn;
#Override
public void start(Stage stage) throws IOException {
try{
conn=java.sql.DriverManager.getConnection("jdbc:sqlite:sample.db");
if(conn!=null){
conn.createStatement().execute("CREATE TABLE IF NOT EXISTS tab1(Field0 TEXT, Field1 TEXT, Field2 TEXT);");
conn.createStatement().execute("CREATE TABLE IF NOT EXISTS tab2(Field3 TEXT, Field4 TEXT);");
}
}catch(Exception ex){
System.out.println(ex);
}
scene = new Scene(loadFXML("main"), 640, 480);
stage.setScene(scene);
stage.show();
}
private static Parent loadFXML(String fxml) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
return fxmlLoader.load();
}
public static void main(String[] args) {
launch();
}
public static ResultSet getRS(String table){
String sql="SELECT * FROM "+table;
ResultSet rs=null;
try{
rs=conn.createStatement().executeQuery(sql);
}catch(Exception ex){
System.out.println(ex);
}
return rs;
}
}
MAIN.FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nic.testfx.Main">
<children>
<MenuBar>
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" onAction="#loadRem" text="Load Remaining" />
</items>
</Menu>
</menus>
</MenuBar>
<TableView fx:id="Tab" prefHeight="200.0" prefWidth="200.0" />
<VBox fx:id="remPane" prefHeight="200.0" prefWidth="100.0" />
</children>
</VBox>
MAIN.JAVA
public class Main implements Initializable {
#FXML
public TableView<ObservableList<String>> Tab;
#FXML
private VBox remPane;
#Override
public void initialize(URL url, ResourceBundle rb) {
Tab.itemsProperty().bind(getTabRow());
}
public void makeTable(ResultSet rs){
try{
var rsm=rs.getMetaData();
ObservableList<TableColumn<ObservableList<String>,String>>cols=FXCollections.observableArrayList();
for(int i=0;i<rsm.getColumnCount();i++){
final int j=i;
TableColumn<ObservableList<String>,String> col=new TableColumn(rsm.getColumnName(i+1));
col.setCellValueFactory(param->new SimpleStringProperty(param.getValue().get(j)));
cols.add(col);
}
ObservableList<ObservableList<String>> arows=FXCollections.observableArrayList();
while(rs.next()){
ObservableList<String> row=FXCollections.observableArrayList();
for(int i=1;i<=rsm.getColumnCount();i++){
row.add(rs.getString(i));
}
arows.add(row);
}
setTable(cols,arows);
}catch(Exception ex){
System.out.println(ex);
}
}
private static final ObservableList<TableColumn<ObservableList<String>,String>> columns=FXCollections.observableArrayList();
private static final javafx.beans.property.ListProperty<ObservableList<String>> rows=new javafx.beans.property.SimpleListProperty();
private javafx.beans.property.ListProperty getTabRow(){
Tab.getColumns().clear();
Tab.getColumns().addAll(columns);
return rows;
}
private static void setTable(ObservableList<TableColumn<ObservableList<String>,String>>c,ObservableList<ObservableList<String>> r){
columns.setAll(c);
rows.set(r);
}
#FXML
private void loadRem(ActionEvent event) {
try{
for(int i=remPane.getChildren().size()-1;i>=0;i--){
remPane.getChildren().remove(i);
}
javafx.fxml.FXMLLoader fxmlLoader=new javafx.fxml.FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("rem.fxml"));
javafx.scene.layout.Pane panel=fxmlLoader.load();
remPane.getChildren().add(panel);
makeTable(App.getRS("tab1"));
}catch(Exception ex){
System.out.println(ex);
}
}
}
REM.FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nic.testfx.Rem">
<children>
<Button fx:id="btn" mnemonicParsing="false" onAction="#loadTab" text="Button" />
</children>
</VBox>
REM.JAVA
public class Rem implements Initializable {
#FXML
private Button btn;
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
private void loadTab(ActionEvent event) {
try{
var fxml=new javafx.fxml.FXMLLoader(App.class.getResource("main.fxml"));
fxml.load();
Main controller=fxml.getController();
var rs=App.getRS("tab2");
controller.makeTable(rs);
}catch(Exception ex){
System.out.println(ex);
}
}
}
OK I got it.
Created a java class which encapsulate method makeTable:
MODEL.JAVA
public class Model{
private final TableView table;
public Model(TableView t){
this.table=t;
}
public void makeTable(ResultSet rs){
ObservableList<TableColumn<ObservableList<String>,String>>tabcols=FXCollections.observableArrayList();
ObservableList<ObservableList<String>>tabrows=FXCollections.observableArrayList();
//Create TableColumns and add to tabcols;
while(rs.next()){
//Create ObservableList<String>, get data from rs, and add to tabrows
}
this.table.getColumns().setAll(tabcols);
this.table.getItems().setAll(tabrows);
}
}
Next, in Controller file Main.java created a static instance of Model:
MAIN.JAVA
public class Main implements Initializable{
#FXML
private TableView<ObservableList<String>> Tab;
static Model model;
#Override
public void initialize (URL url, ResourceBundle rb){
model=new Model(Tab);
}
}
Now I can use model from Rem controller:
REM.JAVA
public class Rem implements Initializable{
#FXML
private Button btn;
#Override
public void initialize (URL url, ResourceBundle rb){
//TODO
}
#FXML
private void loadTab(ActionEvent event){
var rs=App.getRS("tab2");
Main.model.makeTable(rs);
}
Specifically, I have a Primary Stage and two other Stages initialized from the Primary Stage when a menu item is selected.
All three Stages contain TableViews that display different views of my data, and allow relevant actions. When the user is working in one of these Stages and performs an action that changes the data, I would like the changes to be reflected in all three TableViews.
Each of the TableViews is backed by an ObservableArrayList. They update automatically when an element is added or removed, but I have to call the TableView.refresh() method anytime the data changes in any other way and I want it to show.
From reading other posts it seems that it is possible to pass a reference of a Parent Controller object to a Child controller, but it is not considered good practice. It occurred to me that perhaps I could create a new class that would be responsible for refreshing the tables in all 3 Stages, however that would require obtaining a reference to each of the controller objects somehow.
I'm stuck and I'd be grateful for any suggestions!
In attempting to create a minimal reproducible example I figured out what I was doing wrong:
In my original code I was converting Simple Double Properties to Simple String Properties before displaying them in the table, in order to control how they were displayed. The conversion was executed in the overwritten Call() method of Column.setCellValueFactory(). Somehow this conversion was causing the table not to respond to data changes right away.
Here is some code to illustrate what I am talking about:
public class Controller {
#FXML
public TableView<Person> mainTable;
#FXML
public Button editButton;
#FXML
public BorderPane mainBorderPane;
public Button openSecondButton;
public Button refreshButton;
public void initialize(){
DataModel.getInstance().addPerson(new Person("Frank", 1, 20));
DataModel.getInstance().addPerson(new Person("Cindy", 2, 20));
DataModel.getInstance().addPerson(new Person("Eric", 3, 67));
mainTable.setItems(DataModel.getInstance().getPeople());
TableColumn<Person, String> nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>>(){
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> c){
return c.getValue().nameProperty();
}
});
TableColumn<Person, Integer> idColumn = new TableColumn<>("Id");
idColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, Integer>, ObservableValue<Integer>>() {
#Override
public ObservableValue<Integer> call(TableColumn.CellDataFeatures<Person, Integer> person) {
return person.getValue().idProperty().asObject();
}
});
TableColumn<Person, Integer> ageColumn = new TableColumn<>("Age");
ageColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, Integer>, ObservableValue<Integer>>() {
#Override
public ObservableValue<Integer> call(TableColumn.CellDataFeatures<Person, Integer> person) {
return person.getValue().ageProperty().asObject();
}
});
TableColumn<Person, String> ageStringColumn = new TableColumn<>("Age String");
ageStringColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> person) {
return new SimpleStringProperty(String.valueOf(person.getValue().getAge()));
}
});
mainTable.getColumns().addAll(nameColumn, idColumn, ageColumn, ageStringColumn);
}
#FXML
private void showSecondStage(ActionEvent actionEvent) throws IOException {
Stage secondStage = new Stage();
secondStage.setTitle("Secondary Stage");
secondStage.initModality(Modality.NONE);
secondStage.initStyle(StageStyle.UTILITY);
Parent parent = FXMLLoader.load(getClass().getResource("secondary.fxml"));
secondStage.setScene(new Scene(parent));
secondStage.initOwner(mainBorderPane.getScene().getWindow());
secondStage.show();
}
public boolean handleEditPersonRequest() {
Dialog<ButtonType> dialog = new Dialog<>();
dialog.initOwner(mainBorderPane.getScene().getWindow());
dialog.setTitle("Edit Person");
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(Controller.class.getResource("dialog.fxml"));
try {
dialog.getDialogPane().setContent(fxmlLoader.load());
} catch (IOException e) {
e.printStackTrace();
}
DialogController controller = fxmlLoader.getController();
controller.setFields(mainTable.getSelectionModel().getSelectedItem());
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
Button okButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.OK);
okButton.addEventFilter(ActionEvent.ACTION, event -> {
if (!controller.validateAndProcess()) {
event.consume();
System.out.println("Invalid entry, try again");
}});
Optional<ButtonType> result = dialog.showAndWait();
return result.isPresent() && result.get() == ButtonType.OK;
}
public void refreshTable(ActionEvent actionEvent) {
mainTable.refresh();
}
}
And the .fxml file
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane fx:id="mainBorderPane" fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" >
<left>
<VBox>
<Button text="Edit Person" fx:id="editButton" onAction="#handleEditPersonRequest"/>
<Button text = "Open Second Window" fx:id="openSecondButton" onAction="#showSecondStage"/>
<Button text="Refresh table" fx:id="refreshButton" onAction="#refreshTable"/>
</VBox>
</left>
<center>
<TableView fx:id="mainTable" />
</center>
</BorderPane>
Here is the dialog controller:
public class DialogController {
public TextField nameField;
public TextField idField;
public TextField ageField;
public Person person;
public void setFields(Person selectedPerson) {
person = selectedPerson;
nameField.setText(person.getName());
idField.setText(String.valueOf(person.getId()));
ageField.setText(String.valueOf(person.getAge()));
}
public boolean validateAndProcess(){
try{
String name = nameField.getText();
int id = Integer.parseInt(idField.getText());
int age = Integer.parseInt(ageField.getText());
person.setName(name);
person.setId(id);
person.setAge(age);
return true;
}catch (NumberFormatException | NullPointerException e){
e.printStackTrace();
return false;
}
}
}
And it's .fxml file
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="sample.DialogController"
prefHeight="400.0" prefWidth="600.0">
<Label text="Name"/>
<TextField fx:id="nameField"/>
<Label text="Id"/>
<TextField fx:id="idField"/>
<Label text="Age"/>
<TextField fx:id="ageField"/>
</VBox>
I'm not going to include the code for the second window, as it's not needed to see the problem.
I am parsing an XML file and populating the javafx fields with the XML values.
However, these fields are in a tab & I want to clone the tab & its content based on the node count from the XML.
Below is the fxml file screenshot:
I know one method of doing it is have the tab content in a separate FXML file and include it, but the problem of doing so is I have to populate the fields with data & I won't be able to populate data if load same fxml file multiple times with duplicate fx:ids.
Any method by which the above can be achieved?
Here is an example read the comments and you could probably do you fxml in a tab and load it into the tab pane if you want to save yourself a line of code
Main Class
public class Main extends Application {
#Override
public void start(Stage stage) {
TabPane tabPane = new TabPane();
ArrayList<Controller> controllerArrayList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
//Don't just load it into the new node save a reference
FXMLLoader loader = new FXMLLoader(getClass().getResource("/sample.fxml"));
try {
//Load it into the new parent node
Tab tab = new Tab("Tab:"+i, loader.load());
//Save contoller to arraylist of controllers
controllerArrayList.add(loader.getController());
//Add to tabPane
tabPane.getTabs().add(tab);
} catch (IOException e) {
e.printStackTrace();
}
}
//Do some stuff with your contollers
int index = 0;
for (Controller controller : controllerArrayList) {
controller.setLabel("index:"+index);
controller.setTextField("index:"+index++);
}
Scene scene = new Scene(tabPane);
stage = new Stage();
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Controller Class
public class Controller{
public TextField textField;
public Label label;
public void setTextField(String text){ textField.setText(text); }
public void setLabel(String text){ label.setText(text); }
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller">
<children>
<Label fx:id="label"/>
<TextField fx:id="textField" />
</children>
</VBox>
I am trying to add a TableView control to a pre-existing application. I am trying to copy the following example code (which runs perfectly):
public class FxTableViewExample1 extends Application {
private TableView<TransitionRow> outputTable;
private ObservableList<TransitionRow> data;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Table View Example 1");
// Table view, data, columns and properties
outputTable = new TableView<TransitionRow>();
data = getInitialTableData();
outputTable.setItems(data);
TableColumn col1 = new TableColumn("Harland");
col1.setCellValueFactory(new PropertyValueFactory<TransitionRow, Double>("scaleY"));
TableColumn col2 = new TableColumn("Gradstein");
col2.setCellValueFactory(new PropertyValueFactory<TransitionRow, Double>("gradsteinAge"));
TableColumn col3 = new TableColumn("Label");
col3.setCellValueFactory(new PropertyValueFactory<TransitionRow, String>("oldName"));
outputTable.getColumns().setAll(col1, col2, col3);
outputTable.setPrefWidth(450);
outputTable.setPrefHeight(300);
// Vbox
VBox vbox = new VBox(20);
vbox.setPadding(new Insets(25, 25, 25, 25));;
vbox.getChildren().addAll(outputTable);
// Scene
Scene scene = new Scene(vbox, 500, 475); // w x h
primaryStage.setScene(scene);
primaryStage.show();
// Select the first row
outputTable.getSelectionModel().select(0);
TransitionRow tr = outputTable.getSelectionModel().getSelectedItem();
System.out.println(tr);
}
private ObservableList<TransitionRow> getInitialTableData() {
List<TransitionRow> list = new ArrayList<>();
TransitionRow tr1 = new TransitionRow();
tr1.setScaleY((Double) 124.567d);
tr1.setGradsteinAge((Double) 130.001d);
tr1.setOldName("Stuff");
TransitionRow tr2 = new TransitionRow();
tr2.setScaleY((Double) 456.546d);
tr2.setGradsteinAge((Double) 123.768d);
tr2.setOldName("Other stuff");
list.add(tr1);
list.add(tr2);
ObservableList<TransitionRow> data = FXCollections.observableList(list);
return data;
}
}
My app is made via SceneBuilder with separate controller classes. When I tried to integrate the above example the table did not populate so I have created the following minimal example to demonstrate my problem:
[Test1Run.java]
public class Test1Run extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Test1.fxml"));
stage.setTitle("Test1");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
[Test1.fxml]
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.60" fx:controller="experimental.tableview.Test1Controller">
<TableView fx:id="outputTable" layoutX="14.0" layoutY="14.0" prefHeight="371.0" prefWidth="569.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="17.0">
<columns>
<TableColumn fx:id="col1" prefWidth="75.0" text="C1" />
<TableColumn fx:id="col2" prefWidth="75.0" text="C2" />
<TableColumn fx:id="col3" prefWidth="75.0" text="C3" />
</columns>
</TableView>
</AnchorPane>
[Test1Controller.java]
public class Test1Controller implements Initializable {
#FXML private TableView<TransitionRow> outputTable;
#FXML private TableColumn<TransitionRow, Double> col1;
#FXML private TableColumn<TransitionRow, Double> col2;
#FXML private TableColumn<TransitionRow, String> col3;
private ObservableList<TransitionRow> data;
#Override
public void initialize(URL url, ResourceBundle rb) {
outputTable = new TableView<TransitionRow>();
col1 = new TableColumn<TransitionRow,Double>("Harland");
col1.setCellValueFactory(new PropertyValueFactory<TransitionRow, Double>("scaleY"));
col2 = new TableColumn<TransitionRow,Double>("Gradstein");
col2.setCellValueFactory(new PropertyValueFactory<TransitionRow, Double>("gradsteinAge"));
col3 = new TableColumn<TransitionRow,String>("Label");
col3.setCellValueFactory(new PropertyValueFactory<TransitionRow, String>("oldName"));
// This line should cause the column names on the GUI to change. They don't.
outputTable.getColumns().addAll(col1, col2, col3);
data = getInitialTableData();
// This line should cause rows of data to appear on the TableView. It doesn't.
outputTable.setItems(data);
}
private ObservableList<TransitionRow> getInitialTableData() {
List<TransitionRow> list = new ArrayList<>();
TransitionRow tr1 = new TransitionRow();
tr1.setScaleY((Double) 124.567d);
tr1.setGradsteinAge((Double) 130.001d);
tr1.setOldName("Stuff");
TransitionRow tr2 = new TransitionRow();
tr2.setScaleY((Double) 456.546d);
tr2.setGradsteinAge((Double) 123.768d);
tr2.setOldName("Other stuff");
list.add(tr1);
list.add(tr2);
ObservableList<TransitionRow> results = FXCollections.observableList(list);
return results;
}
}
I want it to look like FxTableViewExample1:
But instead it looks like this:
In the initialize you work with new the TableView that you create in the first statement.
You never add this table to the scene though...
The following code should work, assuming the TransitionRow class contains suitable methods for PropertyValueFactory to work.
#Override
public void initialize(URL url, ResourceBundle rb) {
col1.setText("Harland");
col1.setCellValueFactory(new PropertyValueFactory<TransitionRow, Double>("scaleY"));
col2.setText("Gradstein");
col2.setCellValueFactory(new PropertyValueFactory<TransitionRow, Double>("gradsteinAge"));
col3.setText("Label");
col3.setCellValueFactory(new PropertyValueFactory<TransitionRow, String>("oldName"));
data = getInitialTableData();
outputTable.setItems(data);
}
Tableview.getSelectionModel not working. After advice form #James_D I have used a Model class (Software) to select columns I need while pulling SQL to a tableview. I searched here, the web, moved code and checked intellisense and the best examples I can find anywhere are commented out in the SoftwareController code below, nothing works?
Prior to using a Model class I had everyting in the SoftwareController where EXAMPLE 4 worked, it gave the cell data of column 0 where ever on the row I clicked, which I use to pull more SQL data. This now errors at newValue.get(0), newValue is not showing get()or getid is available.
I have changed SelectedItem to index and added toString and all that and I get fxml.software#sometext or the row index. EXAMPLE 1 gives me the cell data of any cell, but I just want the first column on the row I choose, which in my case is an ID, not the row index.
I am also now having to use #SuppressWarnings for "Raw" errors, is this because I am in initialize?
Any help or pointers would be appreciated.
SoftwareController
public class SoftwareController extends Application implements Initializable {
private Statement statement;
Connection conn = null;
#FXML Button btnSoftware;
#FXML Label lblTest;
#FXML TableView tblSoftware;
#FXML TableColumn CI_IDcol;
#FXML TableColumn Namecol;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/Software.fxml")); //load Software fxml file
Parent root1 = (Parent) fxmlLoader.load();
primaryStage.setScene(new Scene(root1));
primaryStage.show();
}
private static ObservableList<Software>data;
#FXML private TextField txtFilter;
private Object getCellData;
#SuppressWarnings({ "unchecked", "rawtypes" }) //added due to TableView getselecionModel code
#Override
public void initialize(URL location, ResourceBundle resources) {
try {
data = FXCollections.observableArrayList();
conn = DBconnection.makeConnection();
statement = conn.createStatement();
String SQL = "SELECT * FROM Data_CMDB_Main";
ResultSet rs = statement.executeQuery(SQL);
while (rs.next()) {
data.add(new Software(rs.getString("CI_ID"),
rs.getString("Name")));
CI_IDcol.setCellValueFactory(new PropertyValueFactory("CI_ID"));
Namecol.setCellValueFactory(new PropertyValueFactory("Name"));
tblSoftware.setItems(null);
tblSoftware.setItems(data);
//TableView.selection
//get row example 1
/*tblSoftware.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observableValue, Object oldValue, Object newValue) {
if(tblSoftware.getSelectionModel().getSelectedItem() != null) {
TableViewSelectionModel selectionModel = tblSoftware.getSelectionModel();
ObservableList selectedCells = selectionModel.getSelectedCells();
TablePosition tablePosition = (TablePosition) selectedCells.get(0);
Object val = tablePosition.getTableColumn().getCellData(newValue);
//Object val = tblSoftware.getColumns().get(0).toString();
System.out.println(val); //int row = tablePosition.getRow();
}
}
});*/
//get row example 2 only gives index of filtered rows
//tblSoftware.getSelectionModel().selectedIndexProperty().addListener((v, oldValue, newValue) -> System.out.println(newValue)); //gets all row data
//get row example 3 ItemProperty seems correct just not giving readable row identification
//tblSoftware.getSelectionModel().selectedItemProperty().addListener((v, oldValue, newValue) -> System.out.println(newValue)); //gets all row data
///get row example 4
//#Override
/*tblSoftware.getSelectionModel().selectedItemProperty().addListener( //gets any row column
(observable, oldValue, newValue) -> {
if (newValue == null) {
lblTest.setText("");
return;
}
lblTest.setText("Selected Number: " + newValue.get(0));
}
);*/
///get row example 5
/*tblSoftware.getSelectionModel().selectedItemProperty().addListener( //gets any row column
new ChangeListener<IdentifiedName>() {
#Override
public void changed (
ObservableValue<? extends IdentifiedName> observable,
IdentifiedName oldValue,
IdentifiedName newValue
){
if(newValue == null) {
lblTest.setText("");
return;
}
lblTest.setText("Selected Number: " + + newValue.getId(0));
}
}
); */
//filter
txtFilter.setPromptText("Text Filter");
txtFilter.textProperty().addListener(new InvalidationListener() {
#Override
public void invalidated(Observable o) {
tblSoftware.getSelectionModel().clearSelection(); // this gives no errors when switching back to filter box when row previously selected
if(txtFilter.textProperty().get().isEmpty()) {
tblSoftware.setItems(data);
return;
}
ObservableList<Software> tableItems = FXCollections.observableArrayList();
ObservableList<TableColumn<Software, ?>> cols = tblSoftware.getColumns();
for(int i=0; i<data.size(); i++) {
for(int j=0; j<cols.size(); j++) {
TableColumn col = cols.get(j);
String cellValue = col.getCellData(data.get(i)).toString();
cellValue = cellValue.toLowerCase();
if(cellValue.contains(txtFilter.textProperty().get().toLowerCase())) {
tableItems.add(data.get(i));
break;
}
}
}
tblSoftware.setItems(tableItems);
}
});
}
} catch (SQLException e) {
e.printStackTrace();
}
}
protected void setIndex(int selectedIndex) {
// TODO Auto-generated method stub
}
public void btnSoftwarePressed(){
lblTest.setText("Button works");
}
}
Software Class
package fxml;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Software {
private StringProperty CI_ID;
private StringProperty Name;
public Software(String CI_ID, String Name) {
this.CI_ID = new SimpleStringProperty(CI_ID);
this.Name = new SimpleStringProperty(Name);
}
public StringProperty CI_IDProperty() {
return CI_ID;
}
public StringProperty NameProperty() {
return Name;
}
}
Software fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java .util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fxml.SoftwareController">
<center>
<TableView fx:id="tblSoftware" prefHeight="200.0" prefWidth="600.0" BorderPane.alignment="CENTER">
<columns>
<TableColumn fx:id="CI_IDcol" prefWidth="100.0" text="CI_ID" />
<TableColumn fx:id="Namecol" prefWidth="150.0" text="Name" />
</columns>
</TableView>
</center>
<top>
<VBox prefHeight="83.0" prefWidth="600.0" BorderPane.alignment="CENTER">
<children>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<Button fx:id="btnSoftware" mnemonicParsing="false" onAction="#btnSoftwarePressed" text="Button" />
<Label fx:id="lblTest" prefHeight="28.0" prefWidth="158.0" text="Label" />
<ParallelCamera />
</children>
</HBox>
<HBox>
<children>
<TextField fx:id="txtFilter" />
</children>
</HBox>
</children>
</VBox>
</top>
</BorderPane>
I'm not really sure of what you want exactly to do, but I can give you my approach to insert data from db into a tableview, and after that, to get the data from db using the previous data loaded into the tableview.
I first created a inner class inside the controller, which will represent the instances to save in/load from database:
public static class Detector {
private String name;
private String conn_type;
private int num_detect;
private String serial_port;
private int data_bits;
private int stop_bits;
private String parity;
private String ip;
private int eth_port;
private int historic;
public Detector(String name, String conn_type, int num_detect, String serial_port, int speed,
int data_bits, int stop_bits, String parity, String ip, int eth_port, int his){
this.name = name;
this.conn_type = conn_type;
this.num_detect = num_detect;
this.serial_port = serial_port;
this.data_bits = data_bits;
this.stop_bits = stop_bits;
this.parity = parity;
this.ip = ip;
this.eth_port = eth_port;
this.historic = his;
}
}
Afther that, I declared the tableview
public class Controller implements Initializable {
#FXML
private TableView<Detector> detectors;
.
.
.
And I created the tableview using the data obtained from a query:
DBConnection c = new DBConnection();
c.connect();
try{
String sql = "select * from detector order by name";
ResultSet rs = c.query(sql);
ObservableList<Detector> data = FXCollections.observableArrayList();
while(rs.next()){
data.add(new Detector(rs.getString("name"),
rs.getString("conn_type"),
Integer.parseInt(rs.getString("num_detect")),
rs.getString("serial_port"),
Integer.parseInt(rs.getString("speed")),
Integer.parseInt(rs.getString("data_bits")),
Integer.parseInt(rs.getString("stop_bits")),
rs.getString("parity"),
rs.getString("ip"),
Integer.parseInt(rs.getString("puerto_socket")),
Integer.parseInt(rs.getString("historico"))
));
}
TableColumn colName = new TableColumn("Name");
colName.setCellValueFactory(new PropertyValueFactory<Detector, String>("name"));
detectors.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> loadName(newValue));
detectors.setItems(data);
detectors.getColumns().addAll(nombreCol);
//Add a column for every column data you want to show
Then, you have to define the behaviour of the method called by the listener (in my case, "loadName")
private void loadName(Detector r){
//here you could, for example, generate a sql query with the data received in r
}