Javafx TableView scroll issue with arrow keys - javafx

I am using JavaFX 11. I have a TableView with a list of mp3 files. While scrolling with arrow keys I am seeing what I think is odd behavior. When I get to the last 'viewable' item in my TableView and hit the down arrow key the focused row jumps up several rows, instead of just one. I have included an image to show what I am trying to explain.
I've added some code which does not match the images I posted but the behavior is exactly the same.
public class FXMLController implements Initializable {
#FXML
private TableView<Person> tableView;
#Override
public void initialize(URL url, ResourceBundle rb) {
TableColumn<Person, String> col1 = new TableColumn<>("Column 1");
col1.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
tableView.getColumns().add(col1);
TableColumn<Person, String> col2 = new TableColumn<>("Column 2");
col2.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
tableView.getColumns().add(col2);
List<Person> list = new ArrayList<Person>();
for (int x = 0; x < 50; x++) {
Person p = new Person();
p.setFirstName("First Name "+x);
p.setLastName("Last Name "+x);
list.add(p);
}
ObservableList<Person> observableList = FXCollections.observableList(list);
tableView.setItems(observableList);
}
Scene.fxml:
<VBox prefHeight="400.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.mycompany.tableviewtest.FXMLController">
<children>
<TableView fx:id="tableView" />
</children>
</VBox>

Related

Is there an effecient way to refresh multiple TableViews across multiple Stages in JavaFX?

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.

Tableview's first column taking a lot of space in javafx 8

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?

TableView created via fxml not populating

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);
}

Javafx SceneBuilder Tableview.getSelectionModel not working

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
}

ChoiceBox: freeze when removing selected item from another ChoiceBox

I want to use two ChoiceBoxes offering the same select items with the exception of the one selected by the other. However, after selecting options a few times, the entire java process becomes unresponsive.
Update: the issue does not occur if I use ComboBox instead of ChoiceBox. However, an explanation for why it happens would be interesting.
I have two ChoiceBoxes
#FXML private ChoiceBox<String> firstCB;
#FXML private ChoiceBox<String> secondCB;
that initially have the same selection options
firstCB.getItems().addAll(Charset.availableCharsets().keySet());
secondCB.getItems().addAll(Charset.availableCharsets().keySet());
and event listeners to remove the new selection from the other ChoiceBox's options and make the old option available again
firstCB.getSelectionModel().selectedItemProperty()
.addListener((observable, oldVal, newVal) -> {
secondCB.getItems().remove(newVal);
secondCB.getItems().add(oldVal);
});
the equivalent Swing code with JComboBoxes and the following event handler works
firstCB.addItemListener(itemEvent -> {
if (itemEvent.getStateChange() == ItemEvent.DESELECTED) {
secondCB.addItem((String) itemEvent.getItem());
} else if (itemEvent.getStateChange() == ItemEvent.SELECTED) {
secondCB.removeItem(itemEvent.getItem());
}
});
Full code:
class test.Main
public class Main extends Application {
private Parent rootPane;
#Override
public void start(Stage arg0) throws Exception {
Scene scene = new Scene(rootPane);
arg0.setScene(scene);
arg0.show();
}
#Override
public void init() throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/main.fxml"));
rootPane = loader.load();
loader.<View>getController().init();
}
public static void main(String[] args) {
launch(args);
}
}
class test.View
public class View {
#FXML
private ChoiceBox<String> firstCB;
#FXML
private ChoiceBox<String> secondCB;
public void init() {
firstCB.getItems().addAll(Charset.availableCharsets().keySet());
secondCB.getItems().addAll(Charset.availableCharsets().keySet());
firstCB.getSelectionModel().selectedItemProperty()
.addListener((observable, oldVal, newVal) -> {
System.out.printf("[%s]firstCB selection changed%n", Thread.currentThread().getName());
secondCB.getItems().remove(newVal);
secondCB.getItems().add(oldVal);
});
// removing one of the event listeners doesn't help
secondCB.getSelectionModel().selectedItemProperty()
.addListener((observable, oldVal, newVal) -> {
System.out.printf("[%s]secondCB selection changed%n", Thread.currentThread().getName());
firstCB.getItems().remove(newVal);
firstCB.getItems().add(oldVal);
});
}
}
main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<Pane fx:id="rootPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="90.0" prefWidth="180.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="test.View">
<children>
<ChoiceBox fx:id="firstCB" layoutX="14.0" layoutY="14.0" prefWidth="150.0" />
<ChoiceBox fx:id="secondCB" layoutX="14.0" layoutY="52.0" prefWidth="150.0" />
</children>
</Pane>
try this, it works fine for me
firstComboBox.valueProperty().addListener((obs, oldV, newV) -> {
secondComboBox.getItems().remove(newV);
if(oldV!= null) secondComboBox.getItems().add(oldV);
});
secondComboBox.valueProperty().addListener((obs, oldV, newV) -> {
firstComboBox.getItems().remove(newV);
if(oldV!= null) firstComboBox.getItems().add(oldV);
});
or other not best solution with observable and filtered list
public class Controller implements Initializable{
public ComboBox<String> firstComboBox;
public ComboBox<String> secondComboBox;
#Override
public void initialize(URL location, ResourceBundle resources) {
ObservableList<String> list = FXCollections.observableArrayList();
list.addAll("one", "two", "three", "four", "five");
firstComboBox.getItems().addAll(list);
secondComboBox.getItems().addAll(list);
firstComboBox.valueProperty().addListener(c -> refreshList(list, secondComboBox, firstComboBox));
secondComboBox.valueProperty().addListener(c -> refreshList(list, firstComboBox, secondComboBox));
}
private void refreshList(ObservableList<String> list, ComboBox<String> choiceBox, ComboBox<String> choiceBox2) {
String tmp = choiceBox.getValue();
FilteredList<String> filteredList = new FilteredList<>(list, string -> !string.equals(choiceBox2.getValue()));
choiceBox.setItems(filteredList);
choiceBox.setValue(tmp);
}

Resources