I did some research on TableView's Filtering and Pagination separately.
Filtering : this post helped me as my need
Pagination : this, this post helped me also
I want to combine them together like so:
In Details --------------
I tried to make the pagination functionality first and it worked,
secondly, when i would start typing to the TextField filter functionality will match/filter the data from ObservableList, then rearrange the pagination according to the matched data size and show them through the table like the Datatable's search and pagination does and that's what i wanted, but i failed
My Code ...
PersonTableController.java
public class PersonTableController {
#FXML private TextField filterField;
#FXML private TableView<Person> personTable;
#FXML private TableColumn<Person, Integer> slColumn;
#FXML private TableColumn<Person, String> firstNameColumn;
#FXML private TableColumn<Person, String> lastNameColumn;
#FXML private Pagination pagination;
private ObservableList<Person> masterData = FXCollections.observableArrayList();
private int dataSize;
private int rowsPerPage = 4;
public PersonTableController() {
masterData.add(new Person(1, "Hans", "Muster"));
masterData.add(new Person(2, "Ruth", "Mueller"));
masterData.add(new Person(3, "Heinz", "Kurz"));
masterData.add(new Person(4, "Cornelia", "Meier"));
masterData.add(new Person(5, "Cornelia", "Meier"));
masterData.add(new Person(6, "Werner", "Meyer"));
masterData.add(new Person(7, "Lydia", "Kunz"));
masterData.add(new Person(8, "Anna", "Best"));
masterData.add(new Person(9, "Stefan", "Meier"));
masterData.add(new Person(10, "Hans", "Muster"));
masterData.add(new Person(11, "Ruth", "Mueller"));
masterData.add(new Person(12, "Heinz", "Kurz"));
masterData.add(new Person(13, "Werner", "Meyer"));
masterData.add(new Person(14, "Lydia", "Kunz"));
}
#FXML
private void initialize() {
dataSize = masterData.size();
pagination.currentPageIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
changeTableView(newValue.intValue(), rowsPerPage);
}
});
FilteredList<Person> filteredData = new FilteredList<>(masterData, p -> true);
filterField.textProperty().addListener((observable, oldValue, newValue) -> {
filteredData.setPredicate(person -> {
if (newValue == null || newValue.isEmpty())
return true;
String lowerCaseFilter = newValue.toLowerCase();
if (person.getFirstName().toLowerCase().indexOf(lowerCaseFilter) != -1) {
return true; // Filter matches first name.
}
return false; // Does not match.
});
});
SortedList<Person> sortedData = new SortedList<>(filteredData);
sortedData.comparatorProperty().bind(personTable.comparatorProperty());
// personTable.setItems(sortedData);
slColumn.setCellValueFactory(new PropertyValueFactory<Person, Integer>("sl"));
firstNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
lastNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
int totalPage = (int) (Math.ceil(dataSize * 1.0 / rowsPerPage));
pagination.setPageCount(totalPage);
pagination.setCurrentPageIndex(0);
changeTableView(0, rowsPerPage);
}
private void changeTableView(int index, int limit) {
int fromIndex = index * limit;
int toIndex = Math.min(fromIndex + limit, dataSize);
List<Person> subListObs = masterData.subList(fromIndex, toIndex);
ObservableList<Person> tmpObsToSetTableVal = FXCollections.observableArrayList();
personTable.getItems().clear();
personTable.setItems(null);
for (Person t : subListObs) {
tmpObsToSetTableVal.add(t);
}
personTable.setItems(tmpObsToSetTableVal);
}
}
Person.java
public class Person {
private final IntegerProperty sl;
private final StringProperty firstName;
private final StringProperty lastName;
public Person(Integer sl, String firstName, String lastName) {
this.sl = new SimpleIntegerProperty(sl);
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
}
public Integer getSl() {
return sl.get();
}
public void setSl(Integer sl) {
this.sl.set(sl);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
}
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Pagination and Filtering");
try {
FXMLLoader loader = new FXMLLoader(Main.class.getResource("PersonTable.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Scene scene = new Scene(page);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
PersonTable.fxml
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Pagination?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<AnchorPane minWidth="315.0" prefHeight="400.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="PersonTableController">
<children>
<HBox id="HBox" alignment="CENTER" spacing="5.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
<children>
<Label text="Filter Table:" />
<TextField fx:id="filterField" prefWidth="-1.0" HBox.hgrow="ALWAYS" />
</children>
</HBox>
<TableView fx:id="personTable" prefHeight="-1.0" prefWidth="-1.0" tableMenuButtonVisible="false" AnchorPane.bottomAnchor="60.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="40.0">
<columns>
<TableColumn fx:id="slColumn" maxWidth="5000.0" minWidth="10.0" prefWidth="120.0" text="SL" />
<TableColumn fx:id="firstNameColumn" maxWidth="5000.0" minWidth="10.0" prefWidth="120.0" text="First Name" />
<TableColumn fx:id="lastNameColumn" maxWidth="5000.0" minWidth="10.0" prefWidth="120.0" text="Last Name" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<HBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
<children>
<Pagination fx:id="pagination" />
</children>
</HBox>
</children>
</AnchorPane>
any help :) plz
here is my simple solution to your problem:
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Pagination;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#FXML
private TextField filterField;
#FXML
private TableView<Person> personTable;
#FXML
private TableColumn<Person, Integer> slColumn;
#FXML
private TableColumn<Person, String> firstNameColumn;
#FXML
private TableColumn<Person, String> lastNameColumn;
#FXML
private Pagination pagination;
private static final int ROWS_PER_PAGE = 4;
private ObservableList<Person> masterData = FXCollections.observableArrayList();
private FilteredList<Person> filteredData;
#Override
public void initialize(URL location, ResourceBundle resources) {
setupData();
filteredData = new FilteredList<>(masterData, p -> true);
filterField.textProperty().addListener((observable, oldValue, newValue) -> {
filteredData.setPredicate(
person -> newValue == null || newValue.isEmpty() || person.getFirstName().toLowerCase()
.contains(newValue.toLowerCase()) || person.getLastName().toLowerCase()
.contains(newValue.toLowerCase()));
changeTableView(pagination.getCurrentPageIndex(), ROWS_PER_PAGE);
});
slColumn.setCellValueFactory(data -> data.getValue().slProperty().asObject());
firstNameColumn.setCellValueFactory(data -> data.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(data -> data.getValue().lastNameProperty());
int totalPage = (int) (Math.ceil(masterData.size() * 1.0 / ROWS_PER_PAGE));
pagination.setPageCount(totalPage);
pagination.setCurrentPageIndex(0);
changeTableView(0, ROWS_PER_PAGE);
pagination.currentPageIndexProperty().addListener(
(observable, oldValue, newValue) -> changeTableView(newValue.intValue(), ROWS_PER_PAGE));
}
public void setupData() {
masterData.add(new Person(1, "Hans", "Muster"));
masterData.add(new Person(2, "Ruth", "Mueller"));
masterData.add(new Person(3, "Heinz", "Kurz"));
masterData.add(new Person(4, "Cornelia", "Meier"));
masterData.add(new Person(5, "Cornelia", "Meier"));
masterData.add(new Person(6, "Werner", "Meyer"));
masterData.add(new Person(7, "Lydia", "Kunz"));
masterData.add(new Person(8, "Anna", "Best"));
masterData.add(new Person(9, "Stefan", "Meier"));
masterData.add(new Person(10, "Hans", "Muster"));
masterData.add(new Person(11, "Ruth", "Mueller"));
masterData.add(new Person(12, "Heinz", "Kurz"));
masterData.add(new Person(13, "Werner", "Meyer"));
masterData.add(new Person(14, "Lydia", "Kunz"));
}
private void changeTableView(int index, int limit) {
int fromIndex = index * limit;
int toIndex = Math.min(fromIndex + limit, masterData.size());
int minIndex = Math.min(toIndex, filteredData.size());
SortedList<Person> sortedData = new SortedList<>(
FXCollections.observableArrayList(filteredData.subList(Math.min(fromIndex, minIndex), minIndex)));
sortedData.comparatorProperty().bind(personTable.comparatorProperty());
personTable.setItems(sortedData);
}
class Person {
private final IntegerProperty sl;
private final StringProperty firstName;
private final StringProperty lastName;
public Person(Integer sl, String firstName, String lastName) {
this.sl = new SimpleIntegerProperty(sl);
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
}
public int getSl() {
return sl.get();
}
public IntegerProperty slProperty() {
return sl;
}
public String getFirstName() {
return firstName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public StringProperty lastNameProperty() {
return lastName;
}
}
}
The table filtering begins on the first page, and if it "overflows" then on the second and so on. This is the expected result?
P.S. I refactored/simplified a little bit the code, like replace the inner classes with lambdas and removed a few unnecessary element + extended the filtering to last name, to get a larger amount of filter possibility, but you can use them as you want.
Related
This question already has answers here:
The table cells are empty in my tableview. JavaFX + Scenebuilder
(2 answers)
Closed 4 years ago.
In my application Some of the columns in my table are populating like they should be the columns labeled Name Priority and BurstTime have the correct values being added but for some reason the other two ProcessID and State do not update with the values I am adding to my ArrayList.
It looks like my code should be working can anybody see something I have missed?
Here is my controller
package application;
import java.net.URL;
import java.util.ResourceBundle;
import java.io.File;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.StringTokenizer;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
public class Controller implements Initializable {
private ArrayList<String> buf = new ArrayList<>();
protected ArrayList<PCB> array = new ArrayList<>();
protected ArrayList<Process> arrayP = new ArrayList<>();
ObservableList<Process> processData = FXCollections.observableArrayList();
#FXML
private Button SubmitButton;
#FXML
private Button LoadButton;
#FXML
private TextArea textArea;
#FXML
private TextField inputBox;
#FXML
private TableView<Process> ProcessTable;
#FXML
private TableColumn<Process, String> processIDP;
#FXML
private TableColumn<Process, String> processTypeP;
#FXML
private TableColumn<Process, String> priorityCodeP;
#FXML
private TableColumn<Process, String> burstTimeP;
#FXML
private TableColumn<Process, String> StatusCodeP;
#Override
public void initialize(URL url, ResourceBundle rb) {
processIDP.setCellValueFactory(new PropertyValueFactory<Process, String>("processIDP"));
processTypeP.setCellValueFactory(new PropertyValueFactory<Process, String>("processTypeP"));
priorityCodeP.setCellValueFactory(new PropertyValueFactory<Process, String>("priorityCodeP"));
burstTimeP.setCellValueFactory(new PropertyValueFactory<Process, String>("burstTimeP"));
StatusCodeP.setCellValueFactory(new PropertyValueFactory<Process, String>("StatusCodeP"));
Process p1 = new Process();
p1.setprocessIDP("22");
p1.setProcessTypeP ("Apname");
p1.setPriorityCodeP("1");
p1.setBurstTimeP ("13");
p1.setstatusCodeP("Tada");
arrayP.add(p1);
ProcessTable.getItems().addAll(arrayP.get(0));
ProcessTable.setItems(FXCollections.observableArrayList(arrayP));
Process p2 = new Process();
p2.setprocessIDP("24");
p2.setProcessTypeP ("Bpname");
p2.setBurstTimeP ("15");
p2.setPriorityCodeP("2");
arrayP.add(p2);
ProcessTable.getItems().addAll(arrayP.get(1));
// edit existing cell ?
arrayP.get(1).setPriorityCodeP("8");
arrayP.get(1).setstatusCodeP("This");
arrayP.get(1).setprocessIDP("TEST");
}
public ObservableList<Process> getProcessData() {
return processData;
}
#FXML
private TextField LoadProgram;
#FXML
private void handleButtonAction() {
textArea.appendText(inputBox.getText() + "\n");
StringTokenizer st1 = new StringTokenizer(inputBox.getText(), " ");
switch(st1.nextToken()) {
// case "proc": proc(); break;
case "mem": textArea.appendText("Memory: " + String.valueOf(Memory.getUsedMemory()) + "/" + String.valueOf(Memory.getTotalMemory()) + "\n"); break;
// case "exe": exe(); break;
// case "reset": reset(); break;
case "load": buf.add(inputBox.getText()) ;
// edit existing cell ?
arrayP.get(1).setPriorityCodeP("9");
ProcessTable.refresh();
break;
case "exit": System.exit(0); break;
case "clear": textArea.clear(); break;
default: break;
}
}
#FXML
private void handleLoadAction() {
File infile = new File("files/" + LoadProgram.getText() + ".txt");
if (infile.exists() == true ) {
textArea.appendText("Loading " + LoadProgram.getText() + "\n");
}
//call to read data here
else {
textArea.appendText("No Program named " + LoadProgram.getText() + " found \n");
}}
public class textLine {
private String infile;
private String cmd, value;
private Scanner input;
public void parseFile(String filename) {
this.infile = "files/" + filename + ".txt";
parseFile();
}
public void addbuf(String textline) {
buf.add(textline);
}
private void parseFile() {
buf.clear();
try {
File file = new File(infile);
if (file.exists() == true)
input = new Scanner(file);
while (input.hasNext()) {
buf.add(input.next());
}
} catch (Exception e) {
e.printStackTrace();
}
input.close();
}
}
}
Here is my class for Procsess
package application;
public class Process {
String processTypeP = "";
String priorityCodeP = "0";
int lineCodeP = 0;
String burstTimeP = "0";
String processIDP = "0";
String StatusCodeP = "0";
public Process (){}
public String getProcessTypeP() {
return processTypeP;
}
public void setProcessTypeP(String processTypeP) {
this.processTypeP = processTypeP;
}
public String getPriorityCodeP() {
return priorityCodeP;
}
public void setPriorityCodeP(String priorityCodeP) {
this.priorityCodeP = priorityCodeP;
}
public int getLineCodeP() {
return lineCodeP;
}
public void setLineCodeP(int lineCodeP) {
this.lineCodeP = lineCodeP;
}
public String getBurstTimeP() {
return burstTimeP;
}
public void setBurstTimeP(String burstTimeP) {
this.burstTimeP = burstTimeP;
}
public String getprocessIDP() {
return processIDP;
}
public void setprocessIDP(String processIDP) {
this.processIDP = processIDP;
}
public String getstatusCodeP() {
return StatusCodeP;
}
public void setstatusCodeP(String StatusCodeP) {
this.StatusCodeP = StatusCodeP;
}
}
Here is my main application
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(Main.class.getResource("/Main.fxml"));
// Parent root = FXMLLoader.load(getClass().getResource("/Main.fxml"));
AnchorPane root = (AnchorPane) loader.load(Main.class.getResource("/application/Main.fxml"));
Scene scene = new Scene(root);
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);
}
}
and my fxml file
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<children>
<TabPane layoutX="4.0" layoutY="4.0" prefHeight="700.0" prefWidth="900.0" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab text="Processes">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<TextField fx:id="inputBox" layoutX="14.0" layoutY="54.0" />
<Button fx:id="SubmitButton" layoutX="109.0" layoutY="94.0" mnemonicParsing="false" onAction="#handleButtonAction" text="Submit" />
<TextArea fx:id="textArea" layoutX="197.0" layoutY="14.0" prefHeight="105.0" prefWidth="493.0" />
<TableView fx:id="ProcessTable" layoutX="36.0" layoutY="157.0" prefHeight="430.0" prefWidth="654.0">
<columns>
<TableColumn fx:id="processIDP" prefWidth="75.0" text="ProccessID" />
<TableColumn fx:id="processTypeP" prefWidth="101.0" text="Name" />
<TableColumn fx:id="priorityCodeP" prefWidth="94.0" text="Priority" />
<TableColumn fx:id="StatusCodeP" prefWidth="119.0" text="State" />
<TableColumn fx:id="burstTimeP" prefWidth="100.0" text="BurstTime" />
</columns>
</TableView>
<TextField fx:id="LoadProgram" layoutX="712.0" layoutY="492.0" />
<Button fx:id="LoadProgramButton" layoutX="725.0" layoutY="531.0" mnemonicParsing="false" onAction="#handleLoadAction" text="Load External Program" />
</children></AnchorPane>
</content>
</Tab>
<Tab text="Scheduler">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
<Tab text="Memory">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>
</children>
</AnchorPane>
Try renaming your getter methods from getprocessIDP and getstatusCodeP to getProcessIDP and getStatusCodeP respectively (note the capital 'S' and 'P'). To avoid problems like this in the future it's a good habit to generate getters, setters and constructors with the IDE instead of doing it manually.
Following code works, but when I change from Male to Female or vice versa, cbSpouse gets new Items loaded. Correct data gets loaded. But the issue is, if I click on the down arrow in cbSpouse, I need to wait for a few minutes before I see the drop down. There is no database interaction, all necessary data is already sitting in Observable list.
ObservableList<String> GenderOptions = FXCollections.observableArrayList("Male", "Female");
cbGender.getItems().clear();
cbGender.setItems(GenderOptions);
cbGender.setVisibleRowCount(GenderOptions.size());
cbGender.getSelectionModel().selectFirst();
cbGender.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue ov, String t, String t1) {
System.out.println("Change Listener for cbGender - oldValue "+t+" new value"+t1);
if ((t1.equals("Male")) || (t.equals(null))) {
cbSpouse.setItems(myMainApp.getFemaleSpouses());
if (thisPerson.getPhoto() == null) {
thePicture.setImage(defaultMale);
}
} else {
cbSpouse.setItems(myMainApp.getMaleSpouses());
if (thisPerson.getPhoto() == null) {
thePicture.setImage(defaultFemale);
}
}
cbSpouse.getSelectionModel().selectFirst();
}
});
//
There are about 300 items in myMainApp.getMaleSpouses() which actually returns the Observable list.
Can someone help me?
Thanks in Advance.
Hornigold
Here is the smaller version, which works nicely. Just keep changing the Gender and click on Spouses, new list appears.
package application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.Tooltip;
import javafx.scene.text.Text;
/**
*
* #author Hornigold Arthur
*/
public class APerson {
private final StringProperty firstName;
private final StringProperty lastName;
private final StringProperty gender;
private final IntegerProperty familyID;
private final IntegerProperty personID;
private final StringProperty toolTipText;
private final Tooltip toolTip;
public APerson() {
this(null, null, null, 0, 0 );
}
/**
* Constructor with some initial data.
*
* #param familyID
* #param personID
* #param firstName
* #param lastName
* #param gender
*/
public APerson( String firstName, String lastName, String gender, int familyID, int personID) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
this.gender = new SimpleStringProperty(gender);
this.familyID = new SimpleIntegerProperty(familyID);
this.personID = new SimpleIntegerProperty(personID);
this.toolTipText = new SimpleStringProperty(null);
this.toolTip = new Tooltip();
}
//
public int getFamilyID() {
return familyID.get();
}
public void setFamilyID(int FamilyID) {
this.familyID.set(FamilyID);
}
public IntegerProperty familyIDProperty() {
return familyID;
}
//
public int getPersonID() {
return personID.get();
}
public void setPersonID(int PersonID) {
this.personID.set(PersonID);
}
public IntegerProperty personIDProperty() {
return personID;
}
//
public String getFirstName() {
return firstName.get().trim();
}
public void setFirstName(String FirstName) {
this.firstName.set(FirstName);
}
public StringProperty firstNameProperty() {
return firstName;
}
//
public String getLastName() {
return lastName.get();
}
public void setLastName(String LastName) {
this.lastName.set(LastName);
}
public StringProperty lastNameProperty() {
return lastName;
}
public String getGender() {
return gender.get();
}
public void setGender(String Gender) {
this.gender.set(Gender);
}
public StringProperty genderProperty() {
return gender;
}
public String LoadToolTipText() {
String tmp = "";
String headerLine = "<body style=\"background-color: LavenderBlush; border-style: none;\"> <u><b><font color=\"red\">Click Mouse's right button to see options</font></b></u><br><br>";
headerLine = headerLine+"<font color=\"red\">Use ARROW keys to scrol the Tooltip</font></b></u><br><br>";
Text t1 = new Text();
t1.setText(headerLine);
String ToolTipText = t1.getText() + this.toString() + "<br>";
ToolTipText = ToolTipText + "<br></body>";
this.toolTip.setStyle("-fx-background-color: pink; -fx-text-fill: black; -fx-font: normal normal 12pt \"Times New Roman\"");
this.toolTip.setText(ToolTipText);
// System.out.println("Tip: "+ToolTipText);
return ToolTipText;
}
public Tooltip getToolTip() {
// A browser.
return toolTip;
}
//
public String toString() {
String tmp = this.getFirstName() + " " + this.getLastName() + " [" + this.getPersonID()+"] ";
return tmp;
}
}
Main.java
package application;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
test000Controller myController;
static private javafx.collections.ObservableList<APerson> persons = javafx.collections.FXCollections.observableArrayList();
static private javafx.collections.ObservableList<APerson> fathers = javafx.collections.FXCollections.observableArrayList();
static private javafx.collections.ObservableList<APerson> mothers = javafx.collections.FXCollections.observableArrayList();
#Override
public void start(Stage stage) throws Exception {
loadFathers();
FXMLLoader loader = new FXMLLoader(getClass().getResource("test000.fxml"));
Parent root = (Parent) loader.load();
myController = loader.getController();
Scene scene = new Scene(root);
myController.persons = persons;
myController.fathers = fathers;
myController.mothers = mothers;
myController.setCBValues();
stage.setTitle("Check editable Combo - TEST000");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private void loadFathers() {
persons.clear();
persons.add(new APerson("*** Not ", "Known ***","F" ,2,0));
persons.add(new APerson("Nesammal", "Rivington","F" ,2,57));
persons.add(new APerson("Ratnammal","Andews","F",1,55));
persons.add(new APerson("Hornigold","Arthur","M", 1,63));
persons.add(new APerson("Andrews","Sundareson","M", 2,60));
persons.add(new APerson("Christopher","Easweradoss","M", 3,57));
persons.add(new APerson("Arthur","Kennedy","M", 4,55));
persons.add(new APerson("Victoria","Arthur","F" , 1,95));
persons.add(new APerson("Eliza", "Daniel","F", 1,60));
fathers.clear();
fathers.add(new APerson("*** Not ", "Known ***","F" ,2,0));
fathers.add(new APerson("Hornigold","Arthur","M", 1,63));
fathers.add(new APerson("Andrews","Sundareson","M", 2,60));
fathers.add(new APerson("Christopher","Easweradoss","M", 3,57));
fathers.add(new APerson("Arthur","Kennedy","M", 4,55));
mothers.clear();
mothers.add(new APerson("*** Not ", "Known ***","F" ,2,0));
mothers.add(new APerson("Nesammal", "Rivington","F" ,2,57));
mothers.add(new APerson("Ratnammal","Andews","F",1,55));
mothers.add(new APerson("Victoria","Arthur","F" , 1,95));
mothers.add(new APerson("Eliza", "Daniel","F", 1,60));
}
}
test000.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="502.0" prefWidth="325.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.test000Controller">
<children>
<VBox prefHeight="431.0" prefWidth="301.0" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="10.0">
<children>
<Label text="Fathers : ">
<font>
<Font name="Times New Roman Bold" size="12.0" />
</font>
</Label>
<ComboBox fx:id="cbFather" editable="true" onAction="#onCBFather" prefHeight="35.0" prefWidth="325.0" promptText="Enter search string" stylesheets="#application.css">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin></ComboBox>
<TextField fx:id="txtValue" editable="false" prefHeight="38.0" prefWidth="301.0" promptText="Selected value" style="-fx-background-color: white; -fx-border-color: red;">
<VBox.margin>
<Insets top="15.0" />
</VBox.margin>
</TextField>
<Label text="Mothers : ">
<font>
<Font name="Times New Roman Bold" size="12.0" />
</font>
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</Label>
<ComboBox fx:id="cbMother" editable="true" onAction="#onCBMother" prefHeight="35.0" prefWidth="325.0" promptText="Enter search string" stylesheets="#application.css">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</ComboBox>
<Label text="Spouses : ">
<font>
<Font name="Times New Roman Bold" size="12.0" />
</font>
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</Label>
<ComboBox fx:id="cbSpouse" editable="true" prefHeight="35.0" prefWidth="325.0" promptText="Enter search string" stylesheets="#application.css">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</ComboBox>
<Label text="Gender:">
<font>
<Font name="Times New Roman Bold" size="12.0" />
</font>
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</Label>
<ComboBox fx:id="cbGender" editable="true" onAction="#onCBGender" onMouseClicked="#handleGenderMoouseEvent" prefHeight="35.0" prefWidth="325.0" promptText="Select Gender" stylesheets="#application.css">
<VBox.margin>
<Insets top="5.0" />
</VBox.margin>
</ComboBox>
</children>
</VBox>
<HBox layoutX="14.0" layoutY="450.0" prefHeight="38.0" prefWidth="294.0">
<children>
<Button fx:id="btnCB1" mnemonicParsing="false" onAction="#showCB1Value" prefHeight="41.0" prefWidth="123.0" text="CB1 value">
<font>
<Font name="System Bold" size="14.0" />
</font>
</Button>
<Button fx:id="btnCB2" mnemonicParsing="false" onAction="#showCB2Value" prefHeight="41.0" prefWidth="123.0" text="CB2 value">
<font>
<Font name="System Bold" size="14.0" />
</font>
<HBox.margin>
<Insets left="25.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</children>
</AnchorPane>
test000Controller.java
package application;
import application.APerson;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.input.InputMethodEvent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class test000Controller {
boolean searchWordStartOnly = true;
private ObservableList<APerson> originalData;
private ObservableList<APerson> originalData2;
public javafx.collections.ObservableList<APerson> persons;
public javafx.collections.ObservableList<APerson> fathers;
public javafx.collections.ObservableList<APerson> mothers;
#FXML
private ResourceBundle resources;
#FXML
private URL location;
#FXML
private ComboBox<APerson> cbFather;
#FXML
private ComboBox<APerson> cbMother;
#FXML
private ComboBox<APerson> cbSpouse;
#FXML
private ComboBox<String> cbGender;
#FXML
private TextField txtValue;
#FXML
private Button btnCB1;
#FXML
private Button btnCB2;
#FXML
void onCBFather(ActionEvent event) {
if (event.getEventType().toString() != null) {
System.out.println("ActionEvent - CBFather - Fired = "+event.getEventType().toString());
}
}
#FXML
void onCBMother(ActionEvent event) {
if (event.getEventType().toString() != null) {
System.out.println("ActionEvent - CBMother - Fired = "+event.getEventType().toString());
}
}
#FXML
void showCB1Value(ActionEvent event) {
txtValue.setText("Button 1 was clicked - "+cbFather.getValue());
}
#FXML
void showCB2Value(ActionEvent event) {
txtValue.setText("Button 2 was clicked - "+cbMother.getValue());
}
#FXML
void onCBGender(ActionEvent event) {
// event.fireEvent(arg0, arg1);
cbGender.getSelectionModel().selectedItemProperty().addListener((options, oldValue, newValue) -> {
System.out.println("cbGender newValue -> " + newValue);
});
}
#FXML
void handleGenderMoouseEvent(MouseEvent event) {
System.out.println("This is MOUSE CLICKED on cbGENDER");
}
#FXML
void initialize() {
assert cbFather != null : "fx:id=\"cbFather\" was not injected: check your FXML file 'test000.fxml'.";
assert txtValue != null : "fx:id=\"txtValue\" was not injected: check your FXML file 'test000.fxml'.";
assert cbMother != null : "fx:id=\"cbMother\" was not injected: check your FXML file 'test000.fxml'.";
assert cbSpouse != null : "fx:id=\"cbSpouse\" was not injected: check your FXML file 'test000.fxml'.";
assert btnCB1 != null : "fx:id=\"btnCB1\" was not injected: check your FXML file 'test000.fxml'.";
assert btnCB2 != null : "fx:id=\"btnCB2\" was not injected: check your FXML file 'test000.fxml'.";
assert cbGender != null : "fx:id=\"cbGender\" was not injected: check your FXML file 'test000.fxml'.";
}
public void setCBValues() {
cbGender.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue ov, String t, String t1) {
System.out.println("Change Listener for cbGender - oldValue "+t+" new value"+t1);
if ((t1.equals("Male")) || (t.equals(null))) {
cbSpouse.setItems(mothers);
} else {
cbSpouse.setItems(fathers);
}
cbSpouse.getSelectionModel().selectFirst();
}
});
//
ObservableList<String> GenderOptions = FXCollections.observableArrayList("Male", "Female");
cbGender.setItems(GenderOptions);
cbGender.setValue("Male");
createCBFather(persons);
createCBMother(persons);
}
public void createCBFather(javafx.collections.ObservableList<APerson> persons) {
cbFather.getItems().clear();
cbFather.setItems(persons);
cbFather.setEditable(true);
cbFather.setMaxWidth(Double.MAX_VALUE);
cbFather.setVisibleRowCount(5);
originalData = cbFather.getItems();
cbFather.getEditor().setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (cbFather.getEditor().getText() == null) {
cbFather.getItems().clear();
cbFather.setItems(persons);
cbFather.setEditable(true);
cbFather.setMaxWidth(Double.MAX_VALUE);
cbFather.setVisibleRowCount(5);
}
System.out.println("MOUSE PRESSED!!! in EDITOR");
}
});
//
cbFather.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (cbFather.getEditor().getText() == null) {
cbFather.getItems().clear();
cbFather.setItems(persons);
cbFather.setEditable(true);
cbFather.setMaxWidth(Double.MAX_VALUE);
cbFather.setVisibleRowCount(5);
}
System.out.println("MOUSE PRESSED!!! in CB BUTTON");
}
});
//
cbFather.getSelectionModel().selectFirst();
//
cbFather.setConverter(new CBFatherConverter());
//
setCellFactory(cbFather);
//
handleKeyPressed(cbFather, originalData);
cbFather.valueProperty().addListener(new ChangeListener<APerson>() {
#Override
public void changed(ObservableValue ov, APerson t, APerson t1) {
System.out.println("Change Listener for cbFather - ov "+t+" new value"+t1);
if (t1 == null) {
cbFather.setItems(persons);
// cbFather.getSelectionModel().selectFirst();;
}
}
});
}
class CBFatherConverter extends StringConverter<APerson> {
#Override
public String toString(APerson object) {
if (object == null) {
cbFather.setPromptText("Use Arrow Keys or type to locate entry");
return null;
}
return object.toString();
}
#Override
public APerson fromString(String thisString) {
// TODO Auto-generated method stub
for (APerson pers : cbFather.getItems()) {
if (pers.toString().equals(thisString)) {
APerson person = pers;
return person;
}
}
return null;
}
}
//
public void createCBMother(javafx.collections.ObservableList<APerson> persons) {
cbMother.setItems(persons);
cbMother.setEditable(true);
cbMother.setMaxWidth(Double.MAX_VALUE);
cbMother.setVisibleRowCount(5);
originalData2 = cbMother.getItems();
cbMother.getSelectionModel().selectFirst();
// if (cbGender.getValue().equals("Male")) {
// cbSpouse.setItems(mothers);
// } else {
// cbSpouse.setItems(fathers);
// }
//
cbMother.setConverter(new CBMotherConverter());
//
setCellFactory(cbMother);
//
handleKeyPressed(cbMother, originalData2);
// cbMother.valueProperty().addListener(new ChangeListener<APerson>() {
// #Override
// public void changed(ObservableValue ov, APerson t, APerson t1) {
// System.out.println("Change Listener for cbMother - ov "+t+" new value"+t1);
// }
// });
//
// cbGender.setValue("Male");
//
}
//
class CBMotherConverter extends StringConverter<APerson> {
#Override
public String toString(APerson object) {
if (object == null) {
cbMother.setPromptText("Use Arrorw Keys or type to locate entry");
return null;
}
return object.toString();
}
#Override
public APerson fromString(String thisString) {
// TODO Auto-generated method stub
for (APerson pers : cbMother.getItems()) {
if (pers.toString().equals(thisString)) {
APerson person = pers;
return person;
}
}
return null;
}
}
//
public void setCellFactory(ComboBox<APerson> thisCombo) {
thisCombo.setCellFactory(new Callback<ListView<APerson>, ListCell<APerson>>() {
#Override
public ListCell<APerson> call(ListView<APerson> param) {
final ListCell<APerson> cell = new ListCell<APerson>() {
#Override
protected void updateItem(APerson item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
final ListCell<APerson> this$ = this;
item.LoadToolTipText();
String htmlString = item.getToolTip().getText();
Tooltip.install(this$, createToolTip(htmlString));
String str = item.toString();
setText(str);
if (item.getGender() == "M") {
String cellStyle = " -fx-font-size: 16;\n"
+ " -fx-font-family: \"Times New Roman\";\n" + "-fx-text-fill: blue;";
setStyle(cellStyle);
} else {
String cellStyle = " -fx-font-size: 16;\n"
+ " -fx-font-family: \"Times New Roman\";\n" + "-fx-text-fill: red;";
setStyle(cellStyle);
}
}
}
}; // ListCell
return cell;
}
}); // setCellFactory
}
public void handleKeyPressed(ComboBox<APerson> thisCombo, ObservableList<APerson> originalData) {
thisCombo.addEventHandler(KeyEvent.KEY_PRESSED, t -> thisCombo.hide());
thisCombo.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {
private boolean moveCaretToPos = false;
private int caretPos;
#Override
public void handle(KeyEvent event) {
switch (event.getCode()) {
case UP:
if (thisCombo.getEditor().equals(null)) {
thisCombo.getSelectionModel().selectFirst();
thisCombo.show();
}
break;
//
case DOWN:
System.out.println("Processing DOWN arrow");
if (thisCombo.getEditor().equals(null)) {
System.out.println("Editor is NULL");
thisCombo.getSelectionModel().selectFirst();
thisCombo.show();
}
if (!thisCombo.isShowing()) {
if (thisCombo.getItems().size() == 0) {
System.out.println("Processing DOWN arrow - Combo Box is EMPTY");
thisCombo.setItems(originalData);
} else {
System.out.println("Processing DOWN arrow - Has items.");
}
thisCombo.getSelectionModel().selectFirst();
thisCombo.show();
}
break;
//
case BACK_SPACE:
moveCaretToPos = true;
caretPos = thisCombo.getEditor().getCaretPosition();
break;
//
case DELETE:
moveCaretToPos = true;
caretPos = thisCombo.getEditor().getCaretPosition();
break;
case ENTER:
break;
//
case RIGHT:
case LEFT:
case SHIFT:
case HOME:
case END:
case TAB:
case CONTROL:
return;
default:
if (event.getCode().ALPHANUMERIC != null) {
// System.out.println("in ALPHANUMERIC input");
locateItems();
}
break;
}
APerson waste = (APerson) getComboBoxValue(thisCombo);
if (waste != null) {
txtValue.setText(waste.getFirstName()+" ID = "+waste.getPersonID());
} else {
txtValue.setText("");
}
}
private void locateItems() {
txtValue.setText("Alpha numeric input");
ObservableList<APerson> list = FXCollections.observableArrayList();
int selectedIndex = 0;
for (APerson aData : originalData) {
String tmp = thisCombo.getEditor().getText().toLowerCase();
if (checkMatch(thisCombo, aData, tmp)) {
list.add(aData);
selectedIndex++;
}
}
String t = thisCombo.getEditor().getText();
thisCombo.setItems(list);
thisCombo.getEditor().setText(t);
if (!moveCaretToPos) {
caretPos = -1;
}
moveCaret(t.length());
if (!list.isEmpty()) {
thisCombo.hide();
if (list.size() > 5) {
thisCombo.setVisibleRowCount(5);
} else {
thisCombo.setVisibleRowCount(list.size());
}
thisCombo.getSelectionModel().select(selectedIndex);
thisCombo.show();
}
}
private void moveCaret(int textLength) {
if (caretPos == -1) {
thisCombo.getEditor().positionCaret(textLength);
} else {
thisCombo.getEditor().positionCaret(caretPos);
}
moveCaretToPos = false;
}
});
}
private boolean checkMatch(ComboBox thisCombo, APerson aData, String tmp) {
boolean retValue = false;
if (aData != null && thisCombo.getEditor().getText() != null) {
String[] words = aData.toString().split(" ");
for (String thisWord : words) {
if (searchWordStartOnly) {
if (thisWord.toLowerCase().startsWith(tmp)) {
return true;
}
} else {
if (thisWord.toLowerCase().contains(tmp)) {
return true;
}
}
}
}
return retValue;
}
public Object getComboBoxValue(ComboBox thisCombo) {
System.out.println("getComboBoxValue - name = "+thisCombo.getSelectionModel().getSelectedItem());
if (thisCombo.getSelectionModel().getSelectedIndex() < 0) {
return null;
} else {
System.out.println("Item Selected in cbFather is = "+thisCombo.getSelectionModel().getSelectedItem());
return thisCombo.getSelectionModel().getSelectedItem();
}
}
//
private Tooltip createToolTip(String htmlStr) {
Tooltip thisToolTip = new Tooltip();
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
webEngine.loadContent(htmlStr);
thisToolTip.setStyle("\n" + " -fx-border-color: black;\n" + " -fx-border-width: 1px;\n"
+ " -fx-font: normal bold 12pt \"Times New Roman\" ;\n"
+ " -fx-background-color: LavenderBlush;\n" + " -fx-text-fill: black;\n"
+ " -fx-background-radius: 4;\n" + " -fx-border-radius: 4;\n" + " -fx-opacity: 1.0;");
thisToolTip.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
thisToolTip.setGraphic(browser);
thisToolTip.setAutoHide(false);
thisToolTip.setMaxWidth(300);
thisToolTip.setPrefHeight(400);
thisToolTip.setGraphicTextGap(0.0);
return thisToolTip;
}
}
In this example data is Hard coded and in Main.java, but in the other version, it is loaded from
static private javafx.collections.ObservableList Fathers = javafx.collections.FXCollections.observableArrayList();
static private javafx.collections.ObservableList Mothers = javafx.collections.FXCollections.observableArrayList();
loading the ObservableList from database occurs much earlier, before cbGender valueProperty Changed.
Thanks,
I'm just starting to work with JavaFX. I create a TableView with 3 columns, (name, last name and select). This last is a Checkbox column. this is my code:
PersonTableController.java:
package ch.makery.sortfilter;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
/**
* View-Controller for the person table.
*
* #author Marco Jakob
*/
public class PersonTableController {
#FXML
private TextField filterField;
#FXML
private TableView<Person> personTable;
#FXML
private TableColumn<Person, String> firstNameColumn;
#FXML
private TableColumn<Person, String> lastNameColumn;
#FXML
private TableColumn<Person, String> selectColumn;
private ObservableList<Person> masterData = FXCollections.observableArrayList();
/**
* Just add some sample data in the constructor.
*/
public PersonTableController() {
masterData.add(new Person("Hans", "Muster"));
masterData.add(new Person("Ruth", "Mueller"));
masterData.add(new Person("Heinz", "Kurz"));
masterData.add(new Person("Cornelia", "Meier"));
masterData.add(new Person("Werner", "Meyer"));
masterData.add(new Person("Lydia", "Kunz"));
masterData.add(new Person("Anna", "Best"));
masterData.add(new Person("Stefan", "Meier"));
masterData.add(new Person("Martin", "Mueller"));
}
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*
* Initializes the table columns and sets up sorting and filtering.
*/
#FXML
private void initialize() {
// 0. Initialize the columns.
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue().firstNameProperty());
lastNameColumn.setCellValueFactory(cellData -> cellData.getValue().lastNameProperty());
selectColumn.setCellValueFactory(
new PropertyValueFactory<Person,String>("select")
);
// 1. Wrap the ObservableList in a FilteredList (initially display all data).
FilteredList<Person> filteredData = new FilteredList<>(masterData, p -> true);
// 2. Set the filter Predicate whenever the filter changes.
filterField.textProperty().addListener((observable, oldValue, newValue) -> {
filteredData.setPredicate(person -> {
// If filter text is empty, display all persons.
if (newValue == null || newValue.isEmpty()) {
return true;
}
// Compare first name and last name of every person with filter text.
String lowerCaseFilter = newValue.toLowerCase();
if (person.getFirstName().toLowerCase().indexOf(lowerCaseFilter) != -1) {
return true; // Filter matches first name.
} else if (person.getLastName().toLowerCase().indexOf(lowerCaseFilter) != -1) {
return true; // Filter matches last name.
}
return false; // Does not match.
});
});
// 3. Wrap the FilteredList in a SortedList.
SortedList<Person> sortedData = new SortedList<>(filteredData);
// 4. Bind the SortedList comparator to the TableView comparator.
// Otherwise, sorting the TableView would have no effect.
sortedData.comparatorProperty().bind(personTable.comparatorProperty());
// 5. Add sorted (and filtered) data to the table.
personTable.setItems(sortedData);
}
}
Person.java:
package ch.makery.sortfilter;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.CheckBox;
/**
* Simple model class for the person table.
*
* #author Marco Jakob
*/
public class Person {
private final StringProperty firstName;
private final StringProperty lastName;
private CheckBox selec = new CheckBox();
public Person(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName;
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName;
}
public CheckBox getSelect() {
return select;
}
public void setSelect(CheckBox select) {
this.select = select;
}
}
PersonTable.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane minWidth="315.0" prefHeight="300.0" prefWidth="315.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.makery.sortfilter.PersonTableController">
<children>
<TableView fx:id="personTable" prefHeight="-1.0" prefWidth="-1.0" tableMenuButtonVisible="false" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="40.0">
<columns>
<TableColumn fx:id="firstNameColumn" maxWidth="5000.0" minWidth="10.0" prefWidth="120.0" text="First Name" />
<TableColumn fx:id="lastNameColumn" maxWidth="5000.0" minWidth="10.0" prefWidth="120.0" text="Last Name" />
<TableColumn fx:id="selectColumn" maxWidth="5000.0" minWidth="10.0" prefWidth="120.0" text="Select" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<HBox id="HBox" alignment="CENTER" spacing="5.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
<children>
<Label text="Filter Table:" />
<TextField fx:id="filterField" prefWidth="-1.0" HBox.hgrow="ALWAYS" />
</children>
</HBox>
</children>
</AnchorPane>
I want than just one person can be selected at the time, so try unsuccessfully to add a event or listener to do that. If any checkbox is checked and user check one of them, is OK. But if one checkbox is checked and other checkbox is checked at same time, then the first one must to be unchecked, just like RadioButton works.
Some one can help me?
Best regards
Thank you #James_D.
finally I can implement the "just-one" checkbox selection. I followed the example than you show me:
javafx-checkboxtablecell-get-actionevent-when-user-check-a-checkbox
And I add this code to call event:
checkedCol.setCellFactory(CheckBoxTableCell.forTableColumn(new Callback<Integer,
ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(Integer param) {
System.out.println("Cours "+coursData.get(param).getCours()+" changed value to " +coursData.get(param).isChecked());
for (Integer i = 0; i < coursData.size(); i++) {
if (i != param && coursData.get(param).isChecked()) {
coursData.get(i).setChecked(false);
} // if
} // for
return coursData.get(param).checkedProperty();
}
}));
in for cicle, I uncheck the rest of checkboxes in table.
Thank you very much for help.
Best Regards.
Leo.
My TableView has a column with a ToggleButton. All the buttons belong to one group, you can only select one button (one row).
But my TableView has a lot of rows and the ToggleGroup seems to work.
That is until I scroll drown.
When I select one ToggleButton and scroll down no other button should be selected but there is always one that is selected per view.
Is this fixable?
Edit: Here is a SSCCE :
MainApp.java :
package p1;
import java.io.IOException;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class MainApp extends Application {
private Stage primaryStage;
private AnchorPane rootLayout;
private ObservableList<Person> personData = FXCollections.observableArrayList();
public MainApp(){
for(int i=0;i<40;i++){
personData.add(new Person("person " +i));
}
}
public ObservableList<Person> getPersonData(){
return personData;
}
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
try{
FXMLLoader loader =new FXMLLoader();
loader.setLocation(MainApp.class.getResource("People.fxml"));
rootLayout = (AnchorPane)loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
PeopleController controller = loader.getController();
controller.setMainApp(this);
} catch(IOException e){
e.printStackTrace();
}
}
public Stage getPrimaryStage(){
return primaryStage;
}
public static void main(String[] args){
launch(args);
}}
People.fxml :
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="p1.PeopleController">
<children>
<TableView fx:id="personTable" layoutX="160.0" layoutY="49.0" prefHeight="351.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="49.0">
<columns>
<TableColumn fx:id="nameColumn" prefWidth="75.0" text="Name" />
<TableColumn fx:id="previewColumn" prefWidth="75.0" text="Preview"/>
</columns>
</TableView>
</children>
</AnchorPane>
PeopleController.java :
package p1;
import com.sun.prism.impl.Disposer;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.util.Callback;
public class PeopleController{
#FXML private TableView<Person> personTable;
#FXML private TableColumn<Person, String> nameColumn;
#FXML private TableColumn previewColumn;
private MainApp mainApp;
final ToggleGroup group = new ToggleGroup();
#FXML
public void initialize() {
nameColumn.setCellValueFactory(cellData -> cellData.getValue().NameProperty());
previewColumn.setCellFactory(
new Callback<TableColumn<Disposer.Record, Boolean>, TableCell<Disposer.Record, Boolean>>() {
#Override
public TableCell<Disposer.Record, Boolean> call(TableColumn<Disposer.Record, Boolean> p) {
ButtonCell cell = new ButtonCell(group);
cell.setAlignment(Pos.CENTER);
return cell;
}
});
}
public void setMainApp(MainApp mainApp){
this.mainApp = mainApp;
personTable.setItems(mainApp.getPersonData());
}
public class ButtonCell extends TableCell<Disposer.Record, Boolean> {
final ToggleButton cellButton = new ToggleButton("click");
public ButtonCell(ToggleGroup group){
cellButton.setToggleGroup(group);
}
#Override
protected void updateItem(Boolean t, boolean empty) {
super.updateItem(t, empty);
if(!empty){
setGraphic(cellButton);
}
}}}
Person.java :
package p1;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty name;
public Person(){
this(null);
}
public Person(String name){
this.name = new SimpleStringProperty(name);
}
public String getName(){
return name.get();
}
public void setName(String name){
this.name.set(name);
}
public StringProperty NameProperty(){
return name;
} }
The reason that the toggle seems to "jump" when scrolling is the re-use of the cells: the selected state sticks to the button, not the item. Consequently, you can't use a toggleGroup (except in the not-so-common and not recommended case that the items in your data implement Toggle) to keep the toggle state. You need to implement the toggle-logic yourself.
One option is a custom SingleSelectionModel and a custom ButtonCell that talks to the model (as do all other collaborators). Unfortunately, FX doesn't have a publicly accessible concrete implementation of the model. As often, the heavy lifting - which in this case is to update itself on modifications to the items - is left to client code (and not done in this example as well ;-)
Something like:
public class ToggleButtonTableExample extends Application {
public static class DataSelectionModel<S> extends SingleSelectionModel<S> {
private ListProperty<S> listProperty;
public DataSelectionModel(Property<ObservableList<S>> items) {
//listProperty = BugPropertyAdapters.listProperty(items);
listProperty = new SimpleListProperty<>();
listProperty.bindBidirectional(items);
ListChangeListener<S> itemsContentObserver = c -> {
itemsChanged(c);
};
listProperty.addListener(itemsContentObserver);
}
protected void itemsChanged(Change<? extends S> c) {
// TODO need to implement update on modificatins to the underlying list
}
#Override
protected S getModelItem(int index) {
if (index < 0 || index >= getItemCount()) return null;
return listProperty.get(index);
}
#Override
protected int getItemCount() {
return listProperty.getSize();
}
}
public static class ButtonCellX<S, T> extends TableCell<S, T> {
private ToggleButton cellButton;
private SingleSelectionModel<S> model;
public ButtonCellX(SingleSelectionModel<S> group) {
this.model = group;
cellButton = new ToggleButton("click");
cellButton.setOnAction(e -> updateToggle());
updateToggle();
setAlignment(Pos.CENTER);
}
protected void updateToggle() {
model.select(cellButton.isSelected()? getIndex() : -1);
}
#Override
protected void updateItem(T t, boolean empty) {
super.updateItem(t, empty);
if (empty) {
setGraphic(null);
} else {
cellButton.setSelected(model.isSelected(getIndex()));
setGraphic(cellButton);
}
}
}
private Parent getContent() {
TableView<Person> table = new TableView<>();
table.setItems(Person.persons());
TableColumn<Person, String> name = new TableColumn<>("Name");
name.setCellValueFactory(new PropertyValueFactory<>("lastName"));
SingleSelectionModel<Person> model = new DataSelectionModel<>(table.itemsProperty());
TableColumn<Person, Boolean> toggle = new TableColumn<>("Preview");
toggle.setCellFactory(c -> new ButtonCellX<Person, Boolean>(model));
toggle.setCellValueFactory(f -> {
Object value = f.getValue();
return Bindings.equal(value, model.selectedItemProperty());
});
table.getColumns().addAll(name, toggle);
Button select = new Button("Select 0");
select.setOnAction(e -> {
model.select(0);
});
VBox content = new VBox(10, table, select);
return content;
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
//primaryStage.setTitle(FXUtils.version());
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(ChoiceBoxTableCellDynamic.class.getName());
}
I currently using afterburner.fx to tailor together components of JavaFX based application.
Right now I trying move components in separate fxml files for more comfortable maintenance.
To load such components I using fx:include directive which allow load nested components automatically.
Problem is that with automatic load I loosing possibility get presenter from nested view.
Is there a way to combine automatic load and in same time, be able work with nested components from parent root?
These two seem to work fine together.
Afterburner works by setting a controller factory on the FXML loader, which takes care of instantiating the presenter class and injecting values into it.
The <fx:include> element will propagate the controller factory when loading the included FXML, so you can also inject values into the controller defined in the included FXML. Because afterburner effectively uses a singleton scope for injection, the same instance of injected fields will be used. This means you can readily share your data model between the different presenter classes.
If you want access to the presenter associated with the included FXML, just use the standard technique for "nested controllers".
So, for example:
main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="application.MainPresenter">
<center>
<TableView fx:id="table">
<columns>
<TableColumn text="First Name" prefWidth="150">
<cellValueFactory>
<PropertyValueFactory property="firstName" />
</cellValueFactory>
</TableColumn>
<TableColumn text="Last Name" prefWidth="150">
<cellValueFactory>
<PropertyValueFactory property="lastName" />
</cellValueFactory>
</TableColumn>
<TableColumn text="Email" prefWidth="200">
<cellValueFactory>
<PropertyValueFactory property="email" />
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</center>
<bottom>
<fx:include source="Editor.fxml" fx:id="editor">
<padding>
<Insets top="5" bottom="5" left="5" right="5"/>
</padding>
</fx:include>
</bottom>
</BorderPane>
editor.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<GridPane xmlns:fx="http://javafx.com/fxml" hgap="5" vgap="10" fx:controller="application.EditorPresenter">
<Label GridPane.rowIndex="0" GridPane.columnIndex="0" text="First Name:"/>
<TextField fx:id="firstNameTextField" GridPane.rowIndex="0" GridPane.columnIndex="1"/>
<Label GridPane.rowIndex="1" GridPane.columnIndex="0" text="Last Name"/>
<TextField fx:id="lastNameTextField" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Label GridPane.rowIndex="2" GridPane.columnIndex="0" text="Email"/>
<TextField fx:id="emailTextField" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<HBox GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2">
<Button fx:id="addEditButton" onAction="#addEdit" />
</HBox>
</GridPane>
MainPresenter.java:
package application;
import javax.inject.Inject;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
public class MainPresenter {
#FXML
private TableView<Person> table ;
// This is the controller (presenter) for the included fxml
// It is injected by the FXMLLoader; the rule is that "Controller" needs to be
// appended to the fx:id attribute of the <fx:include> tag.
// This is not used in this example but is here to demonstrate how to access it
// if needed.
#FXML
private EditorPresenter editorController ;
#Inject
private DataModel dataModel ;
public void initialize() {
table.setItems(dataModel.getPeople());
table.getSelectionModel().selectedItemProperty().addListener(
(obs, oldPerson, newPerson) -> dataModel.setCurrentPerson(newPerson));
dataModel.currentPersonProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson == null) {
table.getSelectionModel().clearSelection();
} else {
table.getSelectionModel().select(newPerson);
}
});
dataModel.getPeople().addAll(
new Person("Jacob", "Smith", "jacob.smith#example.com"),
new Person("Isabella", "Johnson", "isabella.johnson#example.com"),
new Person("Ethan", "Williams", "ethan.williams#example.com"),
new Person("Emma", "Jones", "emma.jones#example.com"),
new Person("Michael", "Brown", "michael.brown#example.com")
);
}
}
EditorPresenter.java:
package application;
import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javax.inject.Inject;
public class EditorPresenter {
#FXML
private TextField firstNameTextField ;
#FXML
private TextField lastNameTextField ;
#FXML
private TextField emailTextField ;
#FXML
private Button addEditButton ;
#Inject
private DataModel dataModel ;
public void initialize() {
addEditButton.textProperty().bind(
Bindings.when(Bindings.isNull(dataModel.currentPersonProperty()))
.then("Add")
.otherwise("Update")
);
dataModel.currentPersonProperty().addListener((obs, oldPerson, newPerson) -> {
if (newPerson == null) {
firstNameTextField.setText("");
lastNameTextField.setText("");
emailTextField.setText("");
} else {
firstNameTextField.setText(newPerson.getFirstName());
lastNameTextField.setText(newPerson.getLastName());
emailTextField.setText(newPerson.getEmail());
}
});
}
#FXML
private void addEdit() {
Person person = dataModel.getCurrentPerson();
String firstName = firstNameTextField.getText();
String lastName = lastNameTextField.getText();
String email = emailTextField.getText();
if (person == null) {
dataModel.getPeople().add(new Person(firstName, lastName, email));
} else {
person.setFirstName(firstName);
person.setLastName(lastName);
person.setEmail(email);
}
}
}
MainView.java:
package application;
import com.airhacks.afterburner.views.FXMLView;
public class MainView extends FXMLView {
}
Main.java (application class):
package application;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import com.airhacks.afterburner.injection.Injector;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
MainView mainView = new MainView();
Scene scene = new Scene(mainView.getView(), 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop() throws Exception {
Injector.forgetAll();
}
public static void main(String[] args) {
launch(args);
}
}
DataModel.java:
package application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class DataModel {
private final ObservableList<Person> people = FXCollections.observableArrayList();
private final ObjectProperty<Person> currentPerson = new SimpleObjectProperty<>(this, "currentPerson");
public ObservableList<Person> getPeople() {
return people ;
}
public final Person getCurrentPerson() {
return currentPerson.get();
}
public final void setCurrentPerson(Person person) {
this.currentPerson.set(person);
}
public ObjectProperty<Person> currentPersonProperty() {
return currentPerson ;
}
}
And the usual Person.java example:
package application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
private final StringProperty firstName = new SimpleStringProperty(this, "firstName");
public final String getFirstName() {
return firstName.get();
}
public final void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public StringProperty firstNameProperty() {
return firstName ;
}
private final StringProperty lastName = new SimpleStringProperty(this, "lastName");
public final String getLastName() {
return lastName.get();
}
public final void setLastName(String lastName) {
this.lastName.set(lastName);
}
public StringProperty lastNameProperty() {
return lastName ;
}
private final StringProperty email = new SimpleStringProperty(this, "email");
public final String getEmail() {
return email.get();
}
public final void setEmail(String email) {
this.email.set(email);
}
public StringProperty emailProperty() {
return email ;
}
public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}
}