JavaFx- Why ChoiceBox value reset inside a tableview after scrolling - java

I have a tableview which have two columns.First column simply populated by observableList and Second column have choiceboxes in each cell.
My problem is that when I select value from choicebox and scroll down to select value from another choicebox and again scroll up then previous selected values of choiceboxe resets.
Below are my code:
#FXML
private TableColumn<FileHeaders, ChoiceBox<String>> fileHeaders;
public void setFileHeaders(ObservableList<String> fileHeadersObservableList) {
fileHeaders.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<FileHeaders, ChoiceBox<String>>, ObservableValue<ChoiceBox<String>>>() {
#Override
public ObservableValue<ChoiceBox<String>> call(TableColumn.CellDataFeatures<FileHeaders, ChoiceBox<String>> rawUdrsList) {
ChoiceBox<String> choiceBox = new ChoiceBox<>();
System.out.println(choiceBox);//this value print again and again when I scroll the tableview
choiceBox.setItems(fileHeadersObservableList);
choiceBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> ov, String t, String valueFromChoiceBox) {
//write some code stuff
}
});
return new SimpleObjectProperty<ChoiceBox<String>>(choiceBox);
}
});
}
As far as I think that call method invoke everytime when i scroll the tableview,so new choicebox created and set into the tableview.
How can i solve this problem,
I really appreciate ifhelps from you guys.
Thanks

Your Error
A cell value factory should not return a node, it should just return the relevant data property backing the cell. Instead, the nodes should be returned in as part of the table cell implementation supplied by a cell factory.
Defining Cell Factories
The makery tutorials provide a nice example which shows the difference in usage between a cell value factory and a cell factory. Usually, the two are used in combination when you require a custom rendering of cell data.
However, JavaFX has predefined helper classes: look at How do you create a table cell factory in JavaFX to display a ChoiceBox?, which uses a ChoiceBoxTableCell. Perhaps your question just works out as a duplicate of that.
Background
To understand how ChoiceBoxTableCell works from first principles, you can look at its source code. A table cell is a Labeled. A Labeled can have both text and a graphic. The text label is used to display the chosen value when the cell is not being edited. Whenever the user double clicks on the cell to edit it, then the text is set to null and a graphic node for the label is displayed instead (in this case a choice box allowing the user to choose a new value for the cell being edited). Once the editing is complete, the new value for the choice is saved to the underlying backing data value property, the display switches back to plain text and the graphic for the cell is set back to null so that it no longer displays. For your use case, it will be easier just to use the pre-defined class rather than to write a custom implementation of the same functionality.
Sample
Output of the sample app below, after the user has twice clicked on a user state which is backed by a choice box, the first click highlighting the row and the second click bringing up the edit choice box for the item:
Sample code demoing use of a ChoiceBoxTableCell:
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.collections.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.*;
import javafx.stage.Stage;
public class TableChoices extends Application {
#Override
public void start(final Stage stage) throws Exception {
ObservableList<User> data = createTestData();
TableColumn<User, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
TableColumn<User, UserState> stateCol = new TableColumn<>("State");
stateCol.setCellValueFactory(new PropertyValueFactory<>("state"));
stateCol.setCellFactory(
ChoiceBoxTableCell.forTableColumn(UserState.values())
);
stateCol.setEditable(true);
stateCol.setPrefWidth(100);
stateCol.setOnEditCommit(
(TableColumn.CellEditEvent<User, UserState> t) ->
t.getTableView()
.getItems()
.get(t.getTablePosition().getRow())
.setState(t.getNewValue())
);
TableView<User> tableView = new TableView<>(data);
//noinspection unchecked
tableView.getColumns().addAll(
nameCol,
stateCol
);
tableView.setPrefSize(170, 150);
tableView.setEditable(true);
stage.setScene(new Scene(tableView));
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
public enum UserState {
ACTIVE,
LOCKED,
DELETED
}
public static class User {
private StringProperty name;
private ObjectProperty<UserState> state;
public User(String name, UserState state) {
this.name = new SimpleStringProperty(name);
this.state = new SimpleObjectProperty<>(state);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public UserState getState() {
return state.get();
}
public ObjectProperty<UserState> stateProperty() {
return state;
}
public void setState(UserState state) {
this.state.set(state);
}
}
private ObservableList<User> createTestData() {
return FXCollections.observableArrayList(
new User("Jack", UserState.ACTIVE),
new User("Jill", UserState.LOCKED),
new User("Tom", UserState.DELETED),
new User("Harry", UserState.ACTIVE)
);
}
}
I advise you to look over the sample code closely and note the use of a setOnEditCommit handler for the table column, which updates the backing data to reflect the edited value. Without code such as this, then the edit will not be committed back to the backing data (at least in Java 8, the JavaFX Table Tutorial notes that future JavaFX versions may make the implementation a bit less clunky).

Related

Is there a way to disable/enable elements in a javaFx application on the startup of said application, i.e Panes and buttons

Ok so this is the first question I've asked on StackOverflow so apologies if its unclear.
Basically, I am making a program in JavaFx that is an ordering system for a fake Cafe. Its for an assignment and it really doesn't have to make conventional sense because my curriculum doesn't really care if it is actually useful or not, they just want to see you code some random stuff.
Anyways, the problem i am having atm is that I am trying to make it so when I open the Main page called MainPage.fxml, 4 things will be pre-disabled/enabled. These elements are PinPane Which contains the sign-in buttons and labels), PrimaryPane (which contains all buttons leading to different ordering pages), SettingsBtn (Sends user to settings), and LogoutBtn (Self expanatory).
This is important because when the program is first opened, MainPage is the first thing that is started. Once a user Signs in, and heads off to another page to select an item however, when they come back to the MainPage, where the current-order is displayed in PrimaryPage (I haven't actually done any code for that yet), I want to ensure that the disabled/enabled states of all 4 elements remains the same as when user left to go to another Page.
Currently, I am using a static class called DataContainer.java, which contains all data shared by the program, and I thought i could put 3 boolean variables which basically just tell the program on the opening of MainPage what is disabled and enabled.
However, my Primary problem is, I can't seem to be able to change the state of any of these elements on startup, and i have no idea how to do that other wise.
My code for the MainPageController.java is below:
'''
public class MainPageController {
#FXML private Label Price; //fx:if -> Price
#FXML public Pane PrimaryPage, PinPane; //fx:id -> PrimaryPage
#FXML public Label Pin; //fx:id -> Pin
#FXML public Button LogoutBtn, SettingsBtn;
public void Check(ActionEvent event) throws IOException{
// This is the method I use to check the entered pin against current
// saved pins.
DataContainer.DataContainer();
// This is just a method i use for testing, it adds a manager account that i can sign in with
// each time the program is opened because I haven't introduced account creation and saving
// yet
int pin = Integer.parseInt(Pin.getText());
int i = DataContainer.Users.size();
int x = 0;
while (x <= i-1){
if (DataContainer.Users.get(x).PinNumber == pin){
// In this, once the pin is verified, each element is enabled and disabled, and the
// boolean variables are set as well for future use
System.out.println("test");
DataContainer.UserIndex = x;
PrimaryPage.setDisable(false);
LogoutBtn.setDisable(false);
DataContainer.PrimaryPage = true;
Pin.setText("");
PinPane.setDisable(true);
DataContainer.PinPane = false;
break;
}
x = x + 1;
}
if (DataContainer.Users.get(DataContainer.UserIndex).Position.equals("Manager")){
SettingsBtn.setDisable(false);
DataContainer.SettingsBtn = true;
}
'''
This is the code for DataContainer.java
'''
public class DataContainer{
public static void main(String args[]){
}
public static void DataContainer(){
Users.add(owner);
System.out.println("test");
}
static boolean PinPane = true, PrimaryPage = false, SettingsBtn = false;
// These boolean values are relevant to the MainPage application
// Their purpose is to retain the information of the state in which the user
// left the main page, i.e, if the PinPage is disabled, the PrimaryPage is enabled, etc.
// this is important as if these variables don't exist the MainPage and its elements
// go back to their default state and the user has to re-sign in.
static String firstname, lastname, position;
static int PinNo, PhoneNo, UserIndex;
public static UserVariables user = new UserVariables(firstname, lastname, position, PinNo,
PhoneNo);
static UserVariables owner = new UserVariables("Test", "User", "Manager", 1234,
0434553);
public static ArrayList<UserVariables> Users = new ArrayList<UserVariables>();
}
'''
And finally this is the code for FInalIA.java (Main class):
'''
public class FInalIA extends Application implements Serializable {
public static void main(String args[]) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
// StackPane root = new StackPane();
Parent root = (Parent) FXMLLoader.load(getClass().getResource("MainPage.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Main Page");
stage.setScene(scene);
stage.setResizable(false);
stage.show();
MainPageController.Open();
}
}
'''
And Finally, this is the code i was thinking of using, by making a public static method called 'Open()', and making all the panes static, and just just calling this method when ever MainPage is opened.
'''
public static void Open(){
if (DataContainer.PinPane == false){
PinPane.setDisable(true);
}
else{
PinPane.setDisable(false);
}
if(DataContainer.PrimaryPage == false){
PrimaryPage.setDisable(true);
LogoutBtn.setDisable(true);
}
else{
PrimaryPage.setDisable(false);
LogoutBtn.setDisable(false);
}
if(DataContainer.SettingsBtn == false){
SettingsBtn.setDisable(true);
}
else{
SettingsBtn.setDisable(false);
}
}
'''
Thanks to whoever helps me out with this (Also can you guys plz tell me if what i am writing is to non-concise and irrelevant or if its actually good)
Step one make Open non-static. You're going to create an instance of your controller and it will manage the associated items.
public class MainPageController implements Initializable{
#FXML private Label price; //fx:if -> Price
#FXML public pane primaryPage, pinPane; //fx:id -> PrimaryPage
#FXML public Label pin; //fx:id -> Pin
#FXML public button bogoutBtn, settingsBtn;
public void Check(ActionEvent event) throws IOException{
//why is this method included but not open?
}
#Override
public void initialize(URL url, ResourceBundle rb){
if (DataContainer.PinPane == false){
pinPane.setDisable(true);
} else{
pinPane.setDisable(false);
}
primaryPage.setDisable( ! DataContainer.PrimaryPage );
logoutBtn.setDisable(! DataContainer.PrimaryPage);
//etc etc
}
}
I've made your controller implement Initializable, that way it has a method initialize that gets called when you start. I've also improved the naming, eg Pin should be named pin. If this doesn't work for you, I can replace this with a small enclosed example.
You don't need to implement Initializable javafx will automatically call an appropriate initialize method.

working with tableView, how to build an application to correctly display information

I understand that the question turned out to be huge, but otherwise it will not be clear to everyone.
I am writing a document manager application for myself, what is happening to me there now - I use the ObservableList collection (located in the main Main class) that operates on variables from the MainData class (it turns out that each instance of this class is a new row in the table). I display all the information through the controller class, that is, I have the first table, where I display all my documents (works), depending on the selected document, I display the necessary information in the corresponding labels (works), but I also there is a second table (it does not work correctly), where, according to my logic, depending on the selected document, I display file names that relate to the selected document (i.e., if one document is highlighted, the second table can display 3 files, and if you select another document, 5 files can be displayed). To check the functionality, I add a couple of rows to the SimpleListProperty collection (which is located in MainData respectively) to display them to the table, but at least my added data in the designer is displayed in the table, they all go in one line, and besides the second table creates the same number of rows as in the first (three identical rows are created in qwerty, asdfgh, since I have three documents created), how to get rid of this dependency? Those. I want to bring my application to the logic that I described above.
Class of all variables
public class MainData {
private final StringProperty numberContract;
private final ListProperty<String> nameFiles; //here I put the names of the files that relate to the document
// further variables for Label
public MainData(String numberContract) {
this.numberContract = new SimpleStringProperty(numberContract);
this.nameFiles = new SimpleListProperty<>(FXCollections.observableArrayList());
this.nameFiles.add("qwerty");
this.nameFiles.add("asdfgh"); //i want to add only two rows to the second table
}
//Further, all getters and setters
Class-controller
public class MainController {
#FXML
private TableView<MainData> contractTable;
#FXML
private TableColumn<MainData, String> numberContractColumn;
#FXML
private TableView<MainData> filesTable;
#FXML
private TableColumn<MainData, ObservableList<String>> nameFilesColumn;
//далее снова все для Label
// Link to main application.
private Main main;
public void setMain(Main main) {
this.main = main;
// Adding to the table data from the observed list
contractTable.setItems(main.getContractData());
filesTable.setItems(main.getContractData());
}
#FXML
private void initialize() {
// Initialize the table.
numberContractColumn.setCellValueFactory(cellData -> cellData.getValue().numberContractProperty());
nameFilesColumn.setCellValueFactory(cellData -> cellData.getValue().getNameFiles());
// Listen to the selection changes, and when changing, display
         // additional information about the addressee.
contractTable.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> showContractDetails(newValue));
Main class
public class Main extends Application {
private Stage menuBar;
private BorderPane mainWindow;
/**
* Data, in the form of an observable list of documents.
*/
private ObservableList<MainData> contractData = FXCollections.observableArrayList();
/**
* Returns data in the form of an observable list of documents.
* #return
*/
public ObservableList<MainData> getContractData() {
return contractData;
}
//further FXML display

JComboBox to display multiple lines of text

I'm currently writing a small tool for sending sql queries to a database and recieving the according data.
Now to my problem:
I want to allow the user to enter a new search query or select from a "latest" list, where the last few queries are saved.
For that, I planned on using an editable JComboBox, but I'm having trouble diplaying multiple lines of text in the box itself.
The reason I want to do that, is because sql queries can get quite long and since I want make the box editable and at the same time keep the frame clean.
I've found ways to display multiple lines in the dropdown menu, but nothing for the box itself.
Thank you in advance and please forgive me if I overlooked something simple ;)
Greetings
Zeus
Extended editing functionality is supplied by the ComboBoxEditor, this allows you to define the actual component which is used as the combobox's editor
Based on your requirements, you're going to need (at the very least) a JTextArea, to provide (optionally) word wrapping and multiple lines
A rough and ready example might look something like this...
public class TextAreaComboBoxEditor implements ComboBoxEditor {
private JTextArea ta = new JTextArea(4, 20);
private JScrollPane sp = new JScrollPane(ta);
public TextAreaComboBoxEditor() {
ta.setWrapStyleWord(true);
ta.setLineWrap(true);
}
#Override
public Component getEditorComponent() {
return sp;
}
#Override
public void setItem(Object anObject) {
if (anObject instanceof String) {
ta.setText((String) anObject);
} else {
ta.setText(null);
}
}
#Override
public Object getItem() {
return ta.getText();
}
#Override
public void selectAll() {
ta.selectAll();
}
#Override
public void addActionListener(ActionListener l) {
}
#Override
public void removeActionListener(ActionListener l) {
}
}
This doesn't support ActionListener, as JTextArea uses the Enter key for it's own purposes. If you wanted to, you could use the key bindings API to add your own Action that can trigger the ActionListeners, for that, you'd need to supply a List or other means for managing them so you can call them back

Why is my JavaFX Tableview not editable?

Why is my TableView not allowing the ID column to be editable? The getPenaltyIdProperty() returns an IntegerProperty (a SimpleIntegerProperty to be exact) and yet I thought this would allow the edits to happen automatically via bindings. what am I missing?
public class PenaltyDashboardManager {
private final TableView<Penalty> penaltyTable = new TableView<Penalty>();
/* ... */
private void initializeTable() {
penaltyTable.setItems(Penalty.getPenaltyManager().getPenalties());
penaltyTable.setEditable(true);
TableColumn<Penalty,Number> penaltyId = new TableColumn<>("ID");
penaltyId.setCellValueFactory(c -> c.getValue().getPenaltyIdProperty());
penaltyId.setEditable(true);
/* ... */
penaltyTable.getColumns.add(penaltyId);
}
}
You need a cell factory to produce a cell that allows the user to edit the value. E.g.
penaltyId.setCellFactory(TextFieldTableCell.forTableColumn(new NumberStringConverter());

Is there a way to implement a property like "rendered" on JAVAFX?

Is there a way to only "render" an component in Javafx if a condition is met?
I am interested in doing a user interface with different roles and just add
a component if the role allows it, also I would like to keep working with FXML.
I haven't read about anything like that.
Bind visibility
Bind the visible property of your component to a BooleanExpression representing the condition under which it should be visible.
FXML can do binding, but note that as of JavaFX 2.2 "Only simple expressions that resolve to property values or page variables are currently supported. Support for more complex expressions involving boolean or other operators may be added in the future."
If you don't bind in FXML, you can bind in Java code using the JavaFX API.
node.visibleProperty().bind(conditionProperty);
If you don't want the invisible property to take up layout space, also first bind the managed property to the visible property.
node.managedProperty().bind(node.visibleProperty());
Alternately change visibility in change listeners
Note that setting up a binding for a full role based system is pretty complex, so for such a task you are better off using change listeners and coding part of your logic in Java rather than trying to do everything in FXML. You will still be able to design your UI in FXML, but you can add some code in your controller to manage what items are visible for what role.
Sample role based display
Here is a role based solution using the Java API.
The solution shows different labeled pictures depending on the selected roles.
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.collections.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.*;
enum Role { father, son, mother, daughter, brother, sister }
class RoleManager {
private final Map<Node, List<Role>> nodeRoles = new HashMap<>();
private ObservableList<Role> activeRoles;
public final ListChangeListener<Role> ACTIVE_ROLE_LISTENER = new ListChangeListener<Role>() {
#Override
public void onChanged(Change<? extends Role> c) {
showActiveNodes();
}
};
public void setActiveRoles(ObservableList<Role> activeRoles) {
if (this.activeRoles != null) {
this.activeRoles.removeListener(ACTIVE_ROLE_LISTENER);
}
this.activeRoles = activeRoles;
this.activeRoles.addListener(ACTIVE_ROLE_LISTENER);
}
public void showActiveNodes() {
for (Node node : nodeRoles.keySet()) {
node.setVisible(isActive(node));
}
}
public void assignRole(Node node, Role... roles) {
nodeRoles.put(node, Arrays.asList(roles));
}
private boolean isActive(Node node) {
if (activeRoles == null) {
return false;
}
for (Role role: nodeRoles.get(node)) {
if (activeRoles.contains(role)) {
return true;
}
}
return false;
}
}
public class RoleVisibility extends Application {
private RoleManager roleManager = new RoleManager();
#Override
public void start(Stage stage) {
VBox layout = new VBox(10);
layout.getChildren().setAll(
getRoleChooser(),
createContent()
);
layout.setStyle("-fx-padding: 10px; -fx-background-color: cornsilk;");
roleManager.showActiveNodes();
stage.setTitle("Role Selector");
stage.setScene(new Scene(layout));
stage.show();
}
private Node getRoleChooser() {
ObservableList<Role> activeRoles = FXCollections.observableArrayList();
VBox roleChooser = new VBox(10);
for (final Role role: Role.values()) {
CheckBox checkBox = new CheckBox(role.toString());
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean wasSelected, Boolean isSelected) {
if (isSelected) {
activeRoles.add(role);
} else {
activeRoles.remove(role);
}
}
});
roleChooser.getChildren().add(checkBox);
}
roleManager.setActiveRoles(
activeRoles
);
return roleChooser;
}
private Pane createContent() {
HBox content = new HBox(10);
// icon license:
//License: Free for non-commercial use.
//Commercial usage: Not allowed
//The products or characters depicted in these icons are © by Lucasfilm Ltd.
content.getChildren().addAll(
createLabel("Darth Vader", "Vader-03-icon.png", Role.father),
createLabel("Queen Amidala", "Padme-Amidala-icon.png", Role.mother),
createLabel("Luke Skywalker", "Luke-Skywalker-01-icon.png", Role.brother, Role.son),
createLabel("Princess Leia", "Leia-icon.png", Role.daughter, Role.sister)
);
return content;
}
private Label createLabel(String text, String graphic, Role... roles) {
Label label = new Label(
text,
new ImageView(
new Image(
"http://icons.iconarchive.com/icons/jonathan-rey/star-wars-characters/128/" + graphic
)
)
);
label.setContentDisplay(ContentDisplay.TOP);
roleManager.assignRole(label, roles);
return label;
}
public static void main(String[] args) {
launch(args);
}
}
FXML Based Solution
I was interested in what it would take to get something like this to work with FXML, so I created a small framework to handle role based FXML UIs. It could perhaps have some performance enhancements and add some convenience definitions for defining role based controllers as well as shorthands for role definitions in the FXML, but it does seem to work in principle and demonstrate a basic approach.

Categories