I have this method in my application which is constructing RadioButtons using an helper class: Mode. I would like to call out of the createModesRadios method .getText() for the user's selected RadioButton. Also I would like to save the user's choices in order to remember them in a following use. Is there an easy way to call them and set the choices into the CheckBoxes, into the RadioButtons and into the PrefixSelectionComboBoxs?
I am using a Model Class Configuration.java in order to store the information, because I will need to process some calculations with the user inputs. I would like to store in it also the choices made from the controllers mentioned above.
Part of my Class
public class ConfigurationEditDialogController {
// General Tab
#FXML
private TextField configurationNameField;
#FXML
private TextField commentField;
#FXML
private TextField creationTimeField;
#FXML
private TextField startAddress;
#FXML
private ToggleSwitch triggerEnable;
#FXML
private ToggleSwitch softwareTrigger;
#FXML
private ToggleSwitch ctsEnable;
// Data Acquisition Tab
#FXML
private Pane dataAcquisitionTab;
#FXML
private Button okButton;
private Mode[] modes = new Mode[]{
new Mode("Vertical", 4),
new Mode("Hybrid", 2),
new Mode("Horizontal", 1)
};
private int count = Stream.of(modes).mapToInt(Mode::getCount).max().orElse(0);
private ToggleGroup group = new ToggleGroup();
private IntegerProperty elementCount = new SimpleIntegerProperty();
private ObservableMap<Integer, String> elements = FXCollections.observableHashMap();
CheckBox[] checkBoxes = new CheckBox[count];
private ObservableList<String> options = FXCollections.observableArrayList(
"ciao",
"hello",
"halo");
private Stage dialogStage;
private Configuration configuration;
private boolean okClicked = false;
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
#FXML
private void initialize() {
HBox radioBox = createModesRadios(elementCount, modes);
GridPane grid = new GridPane();
VBox root = new VBox(20, radioBox);
root.setPrefSize(680, 377);
grid.setHgap(40);
grid.setVgap(30);
grid.setAlignment(Pos.CENTER);
elementCount.addListener((o, oldValue, newValue) -> {
// uncheck checkboxes, if too many are checked
updateCheckBoxes(checkBoxes, newValue.intValue(), -1);
});
for (int i = 0; i < count; i++) {
final Integer index = i;
CheckBox checkBox = new CheckBox();
checkBoxes[i] = checkBox;
PrefixSelectionComboBox<String> comboBox = new PrefixSelectionComboBox<>();
comboBox.setItems(options);
comboBox.setPromptText("Select Test Bus " + i);
comboBox.setPrefWidth(400);
comboBox.setVisibleRowCount(options.size() -1);
comboBox.valueProperty().addListener((o, oldValue, newValue) -> {
// modify value in map on value change
elements.put(index, newValue);
});
comboBox.setDisable(true);
checkBox.selectedProperty().addListener((o, oldValue, newValue) -> {
comboBox.setDisable(!newValue);
if (newValue) {
// put the current element in the map
elements.put(index, comboBox.getValue());
// uncheck checkboxes that exceed the required count keeping the current one unmodified
updateCheckBoxes(checkBoxes, elementCount.get(), index);
} else {
elements.remove(index);
}
});
grid.addRow(i, comboBox, checkBox);
}
// enable Ok button iff the number of elements is correct
okButton.disableProperty().bind(elementCount.isNotEqualTo(Bindings.size(elements)));
root.getChildren().add(grid);
dataAcquisitionTab.getChildren().add(root);
}
/**
* Create Radio Buttons with Mode class helper
*
* #param count
* #param modes
* #return
*/
private HBox createModesRadios(IntegerProperty count, Mode... modes) {
ToggleGroup group = new ToggleGroup();
HBox result = new HBox(50);
result.setPadding(new Insets(20, 0, 0, 0));
result.setAlignment(Pos.CENTER);
for (Mode mode : modes) {
RadioButton radio = new RadioButton(mode.getText());
radio.setToggleGroup(group);
radio.setUserData(mode);
result.getChildren().add(radio);
radio =
}
if (modes.length > 0) {
group.selectToggle((Toggle) result.getChildren().get(0));
count.bind(Bindings.createIntegerBinding(() -> ((Mode) group.getSelectedToggle().getUserData()).getCount(), group.selectedToggleProperty()));
} else {
count.set(0);
}
return result;
}
/**
* Method for updating CheckBoxes depending on the selected modek
*
* #param checkBoxes
* #param requiredCount
* #param unmodifiedIndex
*/
private static void updateCheckBoxes(CheckBox[] checkBoxes, int requiredCount, int unmodifiedIndex) {
if (unmodifiedIndex >= 0 && checkBoxes[unmodifiedIndex].isSelected()) {
requiredCount--;
}
int i;
for (i = 0; i < checkBoxes.length && requiredCount > 0; i++) {
if (i != unmodifiedIndex && checkBoxes[i].isSelected()) {
requiredCount--;
}
}
for (; i < checkBoxes.length; i++) {
if (i != unmodifiedIndex) {
checkBoxes[i].setSelected(false);
}
}
}
/**
* Sets the stage of this dialog.
*
* #param dialogStage
*/
public void setDialogStage(Stage dialogStage) {
this.dialogStage = dialogStage;
}
/**
* Sets the configuration to be edited in the dialog.
*
* #param configuration
*/
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
configurationNameField.setText(configuration.getConfigurationName());
commentField.setText(configuration.getComment());
//Set the comboboxes and the checkboxes and the radiobutton
creationTimeField.setText(LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm")));
}
/**
* Returns true if the user clicked OK, false otherwise.
*
* #return
*/
public boolean isOkClicked() {
return okClicked;
}
/**
* Called when the user clicks ok.
*/
#FXML
private void handleOk() {
if (isInputValid()) {
configuration.setConfigurationName(configurationNameField.getText());
configuration.setComment(commentField.getText());
configuration.setTestBus1(elements.get(0));
configuration.setTestBus2(elements.get(1));
configuration.setTestBus3(elements.get(2));
configuration.setTestBus4(elements.get(3));
configuration.setCreationTime(DateTimeUtil.parse(creationTimeField.getText()));
// Store the information of the sample mode (vertical, hybrid or horizontal).
okClicked = true;
dialogStage.close();
}
}
Mode Helper Class
public class Mode {
private final String text;
private final int count;
public Mode(String text, int count) {
this.text = text;
this.count = count;
}
public String getText() {
return text;
}
public int getCount() {
return count;
}
}
Configuration.java
public class Configuration {
private final StringProperty configurationName;
private final StringProperty comment;
private final StringProperty testBus1;
private final StringProperty testBus2;
private final StringProperty testBus3;
private final StringProperty testBus4;
private final StringProperty triggerSource;
private final StringProperty sampleMode;
private final StringProperty icDesign;
private final StringProperty triggerMode;
private final DoubleProperty acquisitionTime;
private final ObjectProperty<LocalDateTime> creationTime;
/**
* Default constructor.
*/
public Configuration() {
this(null, null);
}
/**
* Constructor with some initial data.
*
* #param configurationName
* #param comment
*/
public Configuration(String configurationName, String comment) { //todo initialize null values
this.configurationName = new SimpleStringProperty(configurationName);
this.comment = new SimpleStringProperty(comment);
// Some initial sample data, just for testing.
this.testBus1 = new SimpleStringProperty("");
this.testBus2 = new SimpleStringProperty("");
this.testBus3 = new SimpleStringProperty("");
this.testBus4 = new SimpleStringProperty("");
this.triggerSource = new SimpleStringProperty("");
this.sampleMode = new SimpleStringProperty("");
this.icDesign = new SimpleStringProperty("Ceres");
this.triggerMode = new SimpleStringProperty("Internal");
this.acquisitionTime = new SimpleDoubleProperty(2346.45);
this.creationTime = new SimpleObjectProperty<>(LocalDateTime.of(2010, 10, 20,
15, 12));
}
/**
* Getters and Setters for the variables of the model class. '-Property' methods for the lambdas
* expressions to populate the table's columns (just a few used at the moment, but everything
* was created with scalability in mind, so more table columns can be added easily)
*/
// If more details in the table are needed just add a column in
// the ConfigurationOverview.fxml and use these methods.
// Configuration Name
public String getConfigurationName() {
return configurationName.get();
}
public void setConfigurationName(String configurationName) {
this.configurationName.set(configurationName);
}
public StringProperty configurationNameProperty() {
return configurationName;
}
// Comment
public String getComment() {
return comment.get();
}
public void setComment (String comment) {
this.comment.set(comment);
}
public StringProperty commentProperty() {
return comment;
}
// Test Bus 1
public String getTestBus1() {
return testBus1.get();
}
public void setTestBus1(String testBus1) {
this.testBus1.set(testBus1);
}
public StringProperty testBus1Property() {
return testBus1;
}
// Test Bus 2
public String getTestBus2() {
return testBus2.get();
}
public void setTestBus2(String testBus2) {
this.testBus2.set(testBus2);
}
public StringProperty testBus2Property() {
return testBus2;
}
// Test Bus 3
public String getTestBus3() {
return testBus3.get();
}
public void setTestBus3(String testBus3) {
this.testBus3.set(testBus3);
}
public StringProperty testBus3Property() {
return testBus3;
}
// Test Bus 4
public String getTestBus4() {
return testBus4.get();
}
public void setTestBus4(String testBus4) {
this.testBus4.set(testBus4);
}
public StringProperty testBus4Property() {
return testBus4;
}
// Trigger Source
public String getTriggerSource(){
return triggerSource.get();
}
public void setTriggerSource (String triggerSource){
this.triggerSource.set(triggerSource);
}
public StringProperty triggerSourceProperty() {
return triggerSource;
}
// Sample Mode
public String getSampleMode() {
return sampleMode.get();
}
public void setSampleMode (String sampleMode){
this.sampleMode.set(sampleMode);
}
public StringProperty sampleModeProperty() {return sampleMode;}
// IC Design
public String getIcDesign() {
return icDesign.get();
}
public void setIcDesign (String icDesign){
this.icDesign.set(icDesign);
}
public StringProperty icDesignProperty() {
return icDesign;
}
// Trigger Mode
public String getTriggerMode() {
return triggerMode.get();
}
public void setTriggerMode (String triggerMode){
this.triggerMode.set(triggerMode);
}
public StringProperty triggerModeProperty() {return triggerMode;}
// Acquisition Time
public double getAcquisitionTime() {
return acquisitionTime.get();
}
public void setAcquisitionTime(double acquisitionTime){
this.acquisitionTime.set(acquisitionTime);
}
public DoubleProperty acquisitionTimeProperty() {
return acquisitionTime;
}
// Last modified - Creation Time
#XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
public LocalDateTime getCreationTime() {
return creationTime.get();
}
public void setCreationTime(LocalDateTime creationTime){
this.creationTime.set(creationTime);
}
public ObjectProperty<LocalDateTime> creationTimeProperty() {
return creationTime;
}
}
You can use the java.util.Properties class to easily read/write settings like this to/from an XML file.
Save To Properties
Properties props = new Properties();
// Set the properties to be saved
props.setProperty("triggerMode", triggerMode.get());
props.setProperty("acquisitionTime", String.valueOf(acquisitionTime.get()));
// Write the file
try {
File configFile = new File("config.xml");
FileOutputStream out = new FileOutputStream(configFile);
props.storeToXML(out,"Configuration");
} catch (IOException e) {
e.printStackTrace();
}
This creates the config.xml file and populates it with all the properties you set using the props.setProperty() method.
Within the props.setProperty() method, you have two parameters. The first is the name of the property, the second is the actual value. The name is important as you will use it to read the appropriate value later.
The above code outputs the following XML file:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Configuration</comment>
<entry key="acquisitionTime">12.0</entry>
<entry key="triggerMode">My Trigger Mode</entry>
</properties>
Read from Properties
Reading from the XML file is just as simple. Use the loadFromXML() method:
FileInputStream in;
// Load the settings file
in = new FileInputStream(DataFiles.LOCAL_SETTINGS_FILE);
properties.loadFromXML(in);
From there, you can set your Configuration model object by getting each property:
configuration.setAcquisitionTime(props.getProperty("acquisitionTime", "0.0"));
configuration.setTriggerMode(props.getProperty("triggerMode", "Manual"));
The getProperty() method also takes two parameters. The first is obviously the name of the property we saved earlier. The second is the default value to use if the requested property name does not exist in the XML file.
Pretty simple!
I do recommend updating all the values in your Configuration.java model and saving properties from there (instead of trying to save the properties directly from your textfields, comboboxes, etc).
EDIT
In order to retrieve the values from your controls within the for loop, you need to add them to a list that is accessible from outside that loop.
Create appropriate lists to hold the controls as their created:
List<PrefixSelectionComboBox<String>> comboBoxes = new ArrayList<PrefixSelectionComboBox<String>>();
List<CheckBox> checkBoxes = new ArrayList<>();
At the bottom of your loop, add the new controls to these lists:
comboBoxes.add(comboBox);
checkBoxes.add(checkBox);
grid.addRow(i, comboBox, checkBox);
Now, when you need their values, you can iterate over those lists to extract them:
for (PrefixSelectionComboBox cb : comboBoxes) {
cb.getValue(); // Do with this what you must
}
for (CheckBox cb : checkBoxes) {
cb.isSelected();
}
Related
I have a TableView which uses a RowFactory to style rows depending on a specific property of the item of the row. The RowFactory uses worker threads to check the validity of this specific property against a call to the database. The problem is that correct rows are sometimes marked as incorrect (red through a PseudoClass) and incorrect rows not marked. I have created a Minimal Reproducible Example below. This example should mark only rows that are even...but it also marks other rows.
Test Entity
public class TestEntity
{
public TestEntity(String firstName, String lastName, int c)
{
setFirstName(firstName);
setLastName(lastName);
setC(c);
}
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
private IntegerProperty c = new SimpleIntegerProperty();
public int getC()
{
return c.get();
}
public IntegerProperty cProperty()
{
return c;
}
public void setC(int c)
{
this.c.set(c);
}
public String getFirstName()
{
return firstName.get();
}
public StringProperty firstNameProperty()
{
return firstName;
}
public void setFirstName(String firstName)
{
this.firstName.set(firstName);
}
public String getLastName()
{
return lastName.get();
}
public StringProperty lastNameProperty()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName.set(lastName);
}
}
Main
public class TableViewProblemMain extends Application
{
public static void main(String[] args)
{
launch(args);
AppThreadPool.shutdown();
}
#Override
public void start(Stage stage)
{
TableView<TestEntity> tableView = new TableView();
TableColumn<TestEntity, String> column1 = new TableColumn<>("First Name");
column1.setCellValueFactory(new PropertyValueFactory<>("firstName"));
TableColumn<TestEntity, String> column2 = new TableColumn<>("Last Name");
column2.setCellValueFactory(new PropertyValueFactory<>("lastName"));
TableColumn<TestEntity, String> column3 = new TableColumn<>("C");
column3.setCellValueFactory(new PropertyValueFactory<>("c"));
tableView.getColumns().addAll(column1, column2, column3);
tableView.setRowFactory(new TestRowFactory());
for (int i = 0; i < 300; i++)
{
tableView.getItems().add(new TestEntity("Fname" + i, "Lname" + i, i));
}
VBox vbox = new VBox(tableView);
Scene scene = new Scene(vbox);
scene.getStylesheets().add(this.getClass().getResource("/style.css").toExternalForm());
// Css has only these lines:
/*
.table-row-cell:invalid {
-fx-background-color: rgba(240, 116, 116, 0.18);
}
* */
stage.setScene(scene);
stage.show();
}
}
Row Factory
public class TestRowFactory implements Callback<TableView<TestEntity>, TableRow<TestEntity>>
{
private final PseudoClass INVALID_PCLASS = PseudoClass.getPseudoClass("invalid");
#Override
public TableRow<TestEntity> call(TableView param)
{
TableRow<TestEntity> row = new TableRow();
Thread validationThread = new Thread(() ->
{
try
{
if(row.getItem() != null)
{
Thread.sleep(500); // perform validation and stuff...
if(row.getItem().getC() % 2 == 0)
{
Tooltip t = new Tooltip("I am a new tooltip that should be shown only on red rows");
row.setTooltip(t);
row.pseudoClassStateChanged(INVALID_PCLASS, true);
}
}
} catch (InterruptedException e)
{
e.printStackTrace();
}
});
ChangeListener changeListener = (obs, old, current) ->
{
row.setTooltip(null);
AppThreadPool.perform(validationThread);
};
row.itemProperty().addListener((observable, oldValue, newValue) ->
{
row.setTooltip(null);
if (oldValue != null)
{
oldValue.firstNameProperty().removeListener(changeListener);
}
if (newValue != null)
{
newValue.firstNameProperty().removeListener(changeListener);
AppThreadPool.perform(validationThread);
}
else
{
row.pseudoClassStateChanged(INVALID_PCLASS, false);
}
});
row.focusedProperty().addListener(changeListener);
return row;
}
}
AppThreadPool
public class AppThreadPool
{
private static final int threadCount = Runtime.getRuntime().availableProcessors();
private static final ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadCount * 2 + 1);
public static <R extends Runnable> void perform(R runnable)
{
executorService.submit(runnable);
}
public static void shutdown()
{
executorService.shutdown();
}
}
Screenshot
There are several misunderstandings in your code. The first is about cells and reuse (a TableRow is a Cell). Cells can be reused arbitrarily, and potentially frequently (especially during user scrolling) to stop displaying one item and display a different one.
In your code, if the row is used to display an entity that is invalid, the listener on the row's itemProperty will trigger the runnable on the background thread, which will at some point set the pseudoclass state to true.
If the cell is subsequently reused to display a valid item, however, the next runnable that is executed does not change the pseudoclass state. So that state remains true and the row color remains red.
Consequently, a row is red if it ever displayed an invalid item at some point. (Not if it is currently displaying an invalid item.) If you scroll enough, eventually all cells will be red.
Secondly, you must not update any UI that is part of the scene graph from any thread other than the FX Application Thread. Additionally, some other operations, such as creating Window instances (Tooltip is a subclass of Window) must be performed on the FX Application Thread. Note that this includes modifying model properties which are bound to the UI, including properties used in the table columns. You violate this in your validationThread, where you create a Tooltip, set it on the row, and change the pseudoclass state, all in a background thread.
A good approach here is to use the JavaFX concurrency API. Use Tasks which, as far as possible, use only immutable data and return an immutable value. If you do need to update properties which are displayed in the UI, use Platform.runLater(...) to schedule those updates on the FX Application Thread.
In terms of MVC design, it is a good practice for your Model class(es) to store all the data needed by the View. Your design runs into trouble because there is no real place where the validation status is stored. Moreover, the validation status is really more than just "valid" or "invalid"; there is a phase while the thread is running but not completed where the validation status is unknown.
Here's my solution, which addresses these issues. I am assuming:
Your entity has a notion of validity.
Establishing the validity of an entity is a long-running process
Validity depends on one or more properties which may change while the UI is displayed
The validity should be "lazily" established, on an as-need basis.
The UI prefers not to display "unknown" validity, and if an entity is displayed whose validity is unknown, it should be established and redisplayed.
I created an enum for ValidationStatus, which has four values:
public enum ValidationStatus {
VALID, INVALID, UNKNOWN, PENDING ;
}
UNKNOWN indicates the validity is not known and validation has not been requested; PENDING indicates that validation has been requested but is not yet complete.
Then I have a wrapper for your entity which adds the validation status as an observable property. If the property on which validation depends in the underlying entity changes, the validation is reset to UNKNOWN.
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class ValidatingTestEntity {
private final TestEntity entity ;
private final ObjectProperty<ValidationStatus> validationStatus = new SimpleObjectProperty<>(ValidationStatus.UNKNOWN);
public ValidatingTestEntity(TestEntity entity) {
this.entity = entity;
entity.firstNameProperty().addListener((obs, oldName, newName) -> setValidationStatus(ValidationStatus.UNKNOWN));
}
public TestEntity getEntity() {
return entity;
}
public ValidationStatus getValidationStatus() {
return validationStatus.get();
}
public ObjectProperty<ValidationStatus> validationStatusProperty() {
return validationStatus;
}
public void setValidationStatus(ValidationStatus validationStatus) {
this.validationStatus.set(validationStatus);
}
}
The ValidationService provides a service to validate entities on a background thread, updating the appropriate properties with the result. This is managed through a thread pool and with JavaFX Tasks. This just mimics a database call by sleeping for a random amount of time and then returning alternating results.
When the task changes state (i.e. as it progresses though its lifecycle), the validation property of the entity is updated: UNKNOWN if the task fails to complete normally, PENDING if the task is in an incomplete state, and either VALID or INVALID, depending on the result of the task, if the task succeeds.
import javafx.application.Platform;
import javafx.concurrent.Task;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
public class ValidationService {
private final Executor exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2 + 1,
r -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
}
);
public Task<Boolean> validateEntity(ValidatingTestEntity entity) {
// task runs on a background thread and should not access mutable data,
// so make final copies of anything needed here:
final String firstName = entity.getEntity().getFirstName();
final int code =entity.getEntity().getC();
Task<Boolean> task = new Task<Boolean>() {
#Override
protected Boolean call() throws Exception {
try {
Thread.sleep(500 + ThreadLocalRandom.current().nextInt(500));
} catch (InterruptedException exc) {
// if interrupted other than being cancelled, reset thread's interrupt status:
if (! isCancelled()) {
Thread.currentThread().interrupt();
}
}
boolean result = code % 2 == 0;
return result;
}
};
task.stateProperty().addListener((obs, oldState, newState) ->
entity.setValidationStatus(
switch(newState) {
case CANCELLED, FAILED -> ValidationStatus.UNKNOWN;
case READY, RUNNING, SCHEDULED -> ValidationStatus.PENDING ;
case SUCCEEDED ->
task.getValue() ? ValidationStatus.VALID : ValidationStatus.INVALID ;
}
)
);
exec.execute(task);
return task ;
}
}
This is the TableRow implementation. It has a listener which observes the validation status of the current item, if there is one. If the item changes, the listener is removed from the old item (if there is one), and attached to the new item (if there is one). If either the item changes, or the validation state of the current item changes, the row is updated. If the new validation status is UNKNOWN, a request is sent to the service to validate the current item. There are two pseudoclass states: invalid (red) and unknown (orange), which are updated any time the item or its validation status change. The tooltip is set if the item is invalid, and set to null otherwise.
import javafx.beans.value.ChangeListener;
import javafx.css.PseudoClass;
import javafx.scene.control.TableRow;
import javafx.scene.control.Tooltip;
public class ValidatingTableRow extends TableRow<ValidatingTestEntity> {
private final ValidationService validationService ;
private final PseudoClass pending = PseudoClass.getPseudoClass("pending");
private final PseudoClass invalid = PseudoClass.getPseudoClass("invalid");
private final Tooltip tooltip = new Tooltip();
private final ChangeListener<ValidationStatus> listener = (obs, oldStatus, newStatus) -> {
updateValidationStatus();
};
public ValidatingTableRow(ValidationService validationService){
this.validationService = validationService ;
itemProperty().addListener((obs, oldItem, newItem) -> {
setTooltip(null);
if (oldItem != null) {
oldItem.validationStatusProperty().removeListener(listener);
}
if (newItem != null) {
newItem.validationStatusProperty().addListener(listener);
}
updateValidationStatus();
});
}
private void updateValidationStatus() {
if (getItem() == null) {
pseudoClassStateChanged(pending, false);
pseudoClassStateChanged(invalid, false);
setTooltip(null);
return ;
}
ValidationStatus validationStatus = getItem().getValidationStatus();
if( validationStatus == ValidationStatus.UNKNOWN) {
validationService.validateEntity(getItem());
}
if (validationStatus == ValidationStatus.INVALID) {
tooltip.setText("Invalid entity: "+getItem().getEntity().getFirstName() + " " +getItem().getEntity().getC());
setTooltip(tooltip);
} else {
setTooltip(null);
}
pseudoClassStateChanged(pending, validationStatus == ValidationStatus.PENDING);
pseudoClassStateChanged(invalid, validationStatus == ValidationStatus.INVALID);
}
}
Here's the Entity, which is the same as in the question:
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class TestEntity
{
public TestEntity(String firstName, String lastName, int c)
{
setFirstName(firstName);
setLastName(lastName);
setC(c);
}
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
private IntegerProperty c = new SimpleIntegerProperty();
public String getFirstName() {
return firstName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public StringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public int getC() {
return c.get();
}
public IntegerProperty cProperty() {
return c;
}
public void setC(int c) {
this.c.set(c);
}
}
And here's the application class. I added the ability to edit the first name, which lets you see an item reverting to unknown and then re-establishing its validity (you need to change selection quickly after committing the edit).
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TableViewProblemMain extends Application
{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage)
{
TableView<ValidatingTestEntity> tableView = new TableView();
tableView.setEditable(true);
TableColumn<ValidatingTestEntity, String> column1 = new TableColumn<>("First Name");
column1.setCellValueFactory(cellData -> cellData.getValue().getEntity().firstNameProperty());
column1.setEditable(true);
column1.setCellFactory(TextFieldTableCell.forTableColumn());
TableColumn<ValidatingTestEntity, String> column2 = new TableColumn<>("Last Name");
column2.setCellValueFactory(cellData -> cellData.getValue().getEntity().lastNameProperty());
TableColumn<ValidatingTestEntity, Number> column3 = new TableColumn<>("C");
column3.setCellValueFactory(cellData -> cellData.getValue().getEntity().cProperty());
tableView.getColumns().addAll(column1, column2, column3);
ValidationService service = new ValidationService();
tableView.setRowFactory(tv -> new ValidatingTableRow(service));
for (int i = 0; i < 300; i++)
{
tableView.getItems().add(new ValidatingTestEntity(
new TestEntity("Fname" + i, "Lname" + i, i)));
}
VBox vbox = new VBox(tableView);
Scene scene = new Scene(vbox);
scene.getStylesheets().add(this.getClass().getResource("/style.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
}
Finally, for completeness, the stylesheet:
.table-row-cell:invalid {
-fx-background-color: rgba(240, 116, 116, 0.18);
}
.table-row-cell:pending {
-fx-background-color: rgba(240, 120, 0, 0.18);
}
I need to show the data from DB in table format with edit, save , cancel, delete button, its called editable grid in wicket.
In Table row after click on edit button data should be shown on 2 dropdown choices and select data from 1st dropdown then 2nd dropdown data model should be changed based on selection of first dropdown.
I have gone through the https://github.com/wicketstuff/core/tree/master/editable-grid-parent. But its showing only editable grid with 1 dropdown in wicket and i want 2 dropdown. Please help on this.
UPDATE :
I have used this
private List<AbstractEditablePropertyColumn<Person, String>> getColumns()
{
List<AbstractEditablePropertyColumn<Person, String>> columns = new ArrayList<AbstractEditablePropertyColumn<Person, String>>();
stateDropDownPropertyColumn = new AbstractEditablePropertyColumn<Person, String>(new PropertyModel<String>(this, "selectedMake"), "state")
{
private static final long serialVersionUID = 1L;
public EditableCellPanel<Person> getEditableCellPanel(String componentId)
{
return getStateDDCellpanel(componentId,this);
}
};
cityDropDownPropertyColumn = new AbstractEditablePropertyColumn<Person, String>(new Model<String>("CarModel"), "city"){
private static final long serialVersionUID = 1L;
#Override
public EditableCellPanel<Person> getEditableCellPanel(String componentId) {
// TODO Auto-generated method stub
return getCityDDCellpanel(componentId,this);
}};
columns.add(stateDropDownPropertyColumn);
columns.add(cityDropDownPropertyColumn);
return columns;
}
private EditableRequiredDropDownCellPanel<Person, String> getStateDDCellpanel(String componentId,
AbstractEditablePropertyColumn<Person, String> DropDownPropertyColumn){
this.stateComponentID = componentId;
this.stateDropDownPropertyColumn = DropDownPropertyColumn;
stateDropDownCellPanel = new EditableRequiredDropDownCellPanel<Person, String>(stateComponentID, stateDropDownPropertyColumn, stateChoices);
return stateDropDownCellPanel;
}
private EditableRequiredDropDownCellPanel<Person, String> getCityDDCellpanel(String componentId,
AbstractEditablePropertyColumn<Person, String> DropDownPropertyColumn){
this.cityComponentID = componentId;
this.cityDropDownPropertyColumn = DropDownPropertyColumn;
cityDropDownCellPanel = new EditableRequiredDropDownCellPanel<Person, String>(cityComponentID, cityDropDownPropertyColumn, cityChoices);
cityDropDownCellPanel.setOutputMarkupId(true);
cityDropDownCellPanel.setOutputMarkupPlaceholderTag(true);
return cityDropDownCellPanel;
}
I have not any idea about this where i need to put up behaviours of dropdown cell.
Probably almost direct answer to question according to dynamic list (first is fixed, second id depended, dynamically computed):
http://examples7x.wicket.apache.org/ajax/choice
To be with SO rules not to include pure links, excerpt from official example Apache licensed
(prepared to be shorter - not compile)
Model allows You prepare changed data, and AJAX event forces refresh - this is core of idea. Event (ajax behaviour) connected to first 'makers', and behaviour forces to refresh 'models' . This is typical wicket+ajax pattern.
/**
* Linked select boxes example
*
* #author Igor Vaynberg (ivaynberg)
*/
public class ChoicePage extends BasePage
{
private String selectedMake;
private final Map<String, List<String>> modelsMap = new HashMap<>(); // map:company->model
/**
* #return Currently selected make
*/
public String getSelectedMake()
{
return selectedMake;
}
/**
* #param selectedMake
* The make that is currently selected
*/
public void setSelectedMake(String selectedMake)
{
this.selectedMake = selectedMake;
}
/**
* Constructor.
*/
public ChoicePage()
{
modelsMap.put("AUDI", Arrays.asList("A4", "A6", "TT"));
modelsMap.put("CADILLAC", Arrays.asList("CTS", "DTS", "ESCALADE", "SRX", "DEVILLE"));
modelsMap.put("FORD", Arrays.asList("CROWN", "ESCAPE", "EXPEDITION", "EXPLORER", "F-150"));
IModel<List<String>> makeChoices = new AbstractReadOnlyModel<List<String>>()
{
#Override
public List<String> getObject()
{
return new ArrayList<>(modelsMap.keySet());
}
};
IModel<List<String>> modelChoices = new AbstractReadOnlyModel<List<String>>()
{
#Override
public List<String> getObject()
{
List<String> models = modelsMap.get(selectedMake);
if (models == null)
{
models = Collections.emptyList();
}
return models;
}
};
Form<?> form = new Form("form");
add(form);
final DropDownChoice<String> makes = new DropDownChoice<>("makes",
new PropertyModel<String>(this, "selectedMake"), makeChoices);
final DropDownChoice<String> models = new DropDownChoice<>("models",
new Model<String>(), modelChoices);
models.setOutputMarkupId(true);
form.add(makes);
form.add(models);
...
makes.add(new AjaxFormComponentUpdatingBehavior("change")
{
#Override
protected void onUpdate(AjaxRequestTarget target)
{
target.add(models);
}
});
}
}
UPDATE after comment. Lets back to previous github code, must replace fixed list by model. How??? Derive or copy, add contructor with IModel ... Wicket has very good object design.
In similar way we add column types not known to (not planned) grid author.
BTW Your comment 'its not working' is very broad. Sorry, I can help if I can, but dont make full project for You. Hope You will enjoy with coding.
package org.wicketstuff.egrid.column;
import java.util.List;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.FormComponent;
/**
*
* #author Nadeem Mohammad
*
*/
public class EditableRequiredDropDownCellPanel<T, S> extends EditableCellPanel
{
private static final long serialVersionUID = 1L;
public EditableRequiredDropDownCellPanel(final String id, final PropertyColumn<T, S> column, #SuppressWarnings("rawtypes") final List choices)
{
super(id);
#SuppressWarnings("unchecked")
DropDownChoice<T> field = new DropDownChoice<T>("dropdown", choices); <--- **** Here should be model ****
field.setLabel(column.getDisplayModel());
add(field);
}
#Override
public FormComponent<?> getEditableComponent()
{
return (FormComponent<?>) get("dropdown");
}
}
then with changed class return here:
columns.add(new AbstractEditablePropertyColumn<Person, String>(new Model<String>("Age"), "age")
{
private static final long serialVersionUID = 1L;
public EditableCellPanel getEditableCellPanel(String componentId)
{
return new ***** EditableRequiredDropDownCellPanel ***** <Person, String>(componentId, this, Arrays.asList("10","11","12","13","14","15"));
}
});
add Beahviours too
I use the afterburner.fx framework for the javafx program as follows,
NameModel used for sharing the state between views or the implementation of business logic.
public class NameModel {
private final StringProperty name = new SimpleStringProperty();
public String getName() {
return name.get();
}
public void setName(String value) {
name.set(value);
}
public StringProperty nameProperty() {
return name;
}
}
SchoolPresenter I used to enter the value for NameModel.If the name in Model get value starting with character
starts with "A",I pass that name to ClassAPresenter and it is similar to names starting with character B, C ...for ClassB,ClassC...
public class SchoolPresenter implements Initializable {
#FXML
private StackPane stackPane;
#Inject NameModel nameModel;
private ClassAView classAView;
private ClassBView classBView;
private ClassCView classCView;
.............................
private ClassNView classNView;
#FXML
void txtName(ActionEvent event) {
String name=txtName.getText();
if (!name.isEmpty()) {
//My program runs fine not wrong but
//If I have N classes, and with declarations for the same classes below
//the program will be very confusing
//Please help me to collapse my code
if(name.startWith("A")){
if (classAView == null) {
classAView = new ClassAView();
ClassAPresenter classAPresenter = (ClassAPresenter) classAView.getPresenter();
classAPresenter.initNameModel(nameModel);
stackPane.getChildren().add(classAView.getView());
}
}else if(name.startWith("B")){
classBView = new ClassBView();
ClassBPresenter classAPresenter = (ClassBPresenter) classBView.getPresenter();
classBPresenter.initNameModel(nameModel);
stackPane.getChildren().add(classBView.getView());
}else if.............//
nameModel.setName(txtName.getText());
}
}
}
The ClassAPresenter, ClassBPresenter ...ClassNPresenter is structured as follows
public class ClassAPresenter implements Initializable {
private NameModel nameModel;
public void initDomainModel(NameModel nameModel) {
// ensure model is only set once:
if (this.nameModel != null) {
throw new IllegalStateException("Model can only be initialized once");
}
this.nameModel = nameModel;
}
}
To give some background: I now am able to load files onto my mp3 program and play them but all the values in my tableview are null?
My song class
package application;
//imports here
public class Song {
private String title;
private String artist;
private String album;
private SimpleStringProperty pTitle;
private SimpleStringProperty pArtist;
private SimpleStringProperty pAlbum;
private Media music;
private MediaPlayer mp;
private Image coverArt;
public Song(File file) {
music = new Media(file.toURI().toString());
music.getMetadata().addListener((Change<? extends String, ? extends Object> c) -> {
if (c.wasAdded()) {
if ("artist".equals(c.getKey())) {
System.out.println(c.getKey()+":"+c.getValueAdded());
this.pArtist = new SimpleStringProperty(c.getValueAdded().toString());
//pArtist.set(c.getValueAdded().toString());
artist = c.getValueAdded().toString();
} else if ("title".equals(c.getKey())) {
title = c.getValueAdded().toString();
System.out.println(c.getKey()+":"+c.getValueAdded());
} else if ("album".equals(c.getKey())) {
album = c.getValueAdded().toString();
System.out.println(c.getKey()+":"+c.getValueAdded());
} else if ("image".equals(c.getKey())) {
coverArt = (Image) c.getValueAdded();
}
}
});
mp = new MediaPlayer(music);
System.out.println(pArtist);
System.out.println(artist);
//artist = (String) mp.getMedia().getMetadata().get("artist");
//title = (String) music.getMetadata().get("title");
//album = (String) music.getMetadata().get("album");
//artist = "test";
//album = "test";
//title = "test";
}
public void play() {
mp.play();
}
public void pause() {
mp.pause();
}
public void stop() {
mp.stop();
}
public String getTitle(){
return title;
}
public void setTitle(String title){
this.title = title;
}
public String getArtist(){
return artist;
}
public void setArtist(String artist){
this.artist = artist;
}
public String getAlbum(){
return album;
}
public void setAlbum(String album){
this.album = album;
}
public Image getCover(){
return coverArt;
}
public MediaPlayer getMP(){
return mp;
}
}
Weirdly enough at first I thought it was because my String variables were not setting correctly and were set to null since it shows as null in the console when I put these print lines to test it when the Song object is being constructed. Here is a sample of the console when I test this.
null
null
artist:Foo Fighters
album:Saint Cecilia EP
title:Saint Cecilia
Here is my controller class
public class SceneController implements Initializable{
#FXML
private Button stopBtn;
#FXML
private Slider volume;
#FXML
private Button loadBtn;
#FXML
private Button playBtn;
#FXML
private TableView<Song> table;
#FXML
private Label label;
#FXML
private ProgressBar proBar;
private TableColumn songCol,artistCol,albumCol;
ObservableList<Song> songList = FXCollections.observableArrayList();
List<File> list;
FileChooser fileChooser = new FileChooser();
Desktop desktop;
Song mySong;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
TableColumn songCol = new TableColumn("Song");
TableColumn artistCol = new TableColumn("Artist");
TableColumn albumCol = new TableColumn("Album");
songCol.setCellValueFactory(
new PropertyValueFactory<Song,String>("title"));
//songCol.setCellFactory(new Callback);
artistCol.setCellValueFactory(
new PropertyValueFactory<Song,String>("artist"));
albumCol.setCellValueFactory(
new PropertyValueFactory<Song,String>("album"));
volume.setMin(0);
volume.setMax(100);
volume.setValue(100);
volume.valueProperty().addListener(new InvalidationListener() {
#Override
public void invalidated(Observable observable) {
mySong.getMP().setVolume(volume.getValue()/100.0);
}
});
}
// Event Listener on Button[#loadBtn].onAction
#FXML
public void loadFile(ActionEvent event) {
Node source = (Node) event.getSource();
Window theStage = source.getScene().getWindow();
//set fileChooser filter
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("MP3 files", "*.mp3");
fileChooser.getExtensionFilters().add(extFilter);
fileChooser.setTitle("Select MP3 files");
//File file = fileChooser.showOpenDialog(theStage);
//mySong = new Song(file);
list = fileChooser.showOpenMultipleDialog(theStage);
if(list!=null){
for(File x: list) {
mySong = new Song(x);
System.out.println(mySong.getTitle());
songList.add(mySong);
}
}
table.setItems(songList);
}
#FXML
public void playSong(ActionEvent event) {
mySong.play();
}
#FXML
public void stopSong(ActionEvent event) {
//mySong.pause();
System.out.println("song title: "+mySong.getArtist()+mySong.getTitle());
ImageView img = new ImageView(mySong.getCover());
//img.fitWidthProperty().bind(label.widthProperty());
//img.fitHeightProperty().bind(label.heightProperty());
img.setFitHeight(120);
img.setFitWidth(200);
label.setGraphic(img);
//label.setGraphic(new ImageView(mySong.getCover()));
}
But I made another test print line for my "Stop" button in the controller class and after everything is loaded and I press it, it prints out the artist and title fine. I have saw this other thread and checked my getter methods and they seem to be correct? I am really lost on this and if anyone could provide some insight and a solution as to whether it is because my variables are null or my PropertyValueFactory is not done correctly
Also I notice that the nulls come first even though should they not be the last thing printed since when I create a new song object in my controller class the first print lines that run are in the if statements?
There are several things wrong with the way you have your current code, that are evident from the limited example you posted in the question:
Your Song class does not properly follow the JavaFX properties pattern. In particular, you store each "property" twice, once in a "traditional" JavaBean-style field, for example private String title, and once in a JavaFX property: private StringProperty pTitle;. Each property should be stored once. If you want the table to be aware when the value changes, you should use JavaFX properties, and have the "standard" getXXX() and setXXX() retrieve and set the underlying values stored in those properties.
The listener you attach to the media's metadata is called asynchronously at some indeterminate point in the future. When you add the song to the table's list, the cell value factories attached to the columns will, at some point, be executed, and retrieve the assigned property from the Song instance. With the code the way you currently have it, those property instances are only actually created once the listener on the metadata is invoked. So it is possible (perhaps likely) that the cell value factory will inspect the Song instance for its property before the JavaFX property is instantiated, making it impossible for the table to properly observe the property and respond to changes in it. You should instantiate the JavaFX properties when the Song instance is created, and set their value in the listener on the metadata.
At no point do you add the columns you create in the controller to the table. If you are creating them in the FXML file (which you didn't post in the question), you should inject those columns into the controller and initialize those columns with the cell value factories. (Since the screenshot shows there are columns in the table, I am going to assume they are defined in the FXML file, and have appropriate fx:ids.)
So your Song class should look something like this:
public class Song {
private final StringProperty title = new SimpleStringProperty();
private final StringProperty artist = new SimpleStringProperty();
private final StringProperty album = new SimpleStringProperty();
private Media music;
private MediaPlayer mp;
private Image coverArt;
public Song(File file) {
music = new Media(file.toURI().toString());
music.getMetadata().addListener((Change<? extends String, ? extends Object> c) -> {
if (c.wasAdded()) {
if ("artist".equals(c.getKey())) {
setArtist(c.getValueAdded().toString());
} else if ("title".equals(c.getKey())) {
setTitle(c.getValueAdded().toString());
} else if ("album".equals(c.getKey())) {
setAlbum(c.getValueAdded().toString());
} else if ("image".equals(c.getKey())) {
// maybe this needs to be a JavaFX property too: it is not clear from your question:
coverArt = (Image) c.getValueAdded();
}
}
});
mp = new MediaPlayer(music);
}
public void play() {
mp.play();
}
public void pause() {
mp.pause();
}
public void stop() {
mp.stop();
}
public StringProperty titleProperty() {
return title ;
}
public final String getTitle(){
return titleProperty().get();
}
public final void setTitle(String title){
titleProperty().set(title);
}
public StringProperty artistProperty() {
return artist ;
}
public final String getArtist(){
return artistProperty().get();
}
public final void setArtist(String artist){
artistProperty.set(artist);
}
public StringProperty albumProperty() {
return album ;
}
public final String getAlbum(){
return albumProperty().get();
}
public final void setAlbum(String album){
albumProperty().set(album);
}
public Image getCover(){
return coverArt;
}
public MediaPlayer getMP(){
return mp;
}
}
For your controller, I am going to assume your FXML file has defined table columns with fx:ids of "songCol", "artistCol", and "albumCol", respectively. You need to inject these into the controller as you do with the other columns. I also strongly recommend not using the PropertyValueFactory class, which uses reflection and lacks much in the way of compile-time checking, and implementing the callback yourself. Using lambda expressions makes this pretty easy.
So your controller should look like:
public class SceneController implements Initializable{
// non-table code omitted...
#FXML
private TableView<Song> table;
#FXML
private Label label;
#FXML
private ProgressBar proBar;
#FXML
private TableColumn<Song, String> songCol ;
#FXML
private TableColumn<Song, String> artistCol ;
#FXML
private TableColumn<Song, String> albumCol;
ObservableList<Song> songList = FXCollections.observableArrayList();
List<File> list;
FileChooser fileChooser = new FileChooser();
Desktop desktop;
Song mySong;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
songCol.setCellValueFactory(cellData -> cellData.getValue().titleProperty());
artistCol.setCellValueFactory(cellData -> cellData.getValue().artistProperty());
albumCol.setCellValueFactory(cellData -> cellData.getValue().albumProperty());
// ...
}
// other non-table code omitted...
}
You didn't post an minimal, complete, verifiable example, so there may well be other errors in your code which prevent the table from displaying correctly. This should get you started, however.
Normally the TableColumns would be defined in FXML and injected via #FXML.
See the Oracle TableView FXML example.
If you don't want to do it that way, you need to do:
table.getColumns().add(songCol);
And similarly for your other columns.
Also, as HypnicJerk pointed out in comments you also need to follow appropriate naming conventions when using the PropertyValueFactory.
songCol.setCellValueFactory(
new PropertyValueFactory<Song,String>("title")
);
For more details see:
Javafx tableview not showing data in all columns
I am making a program that returns the output in a long string variable. The data in the string is constantly changing based on what the user enters in the GUI. My question is, how do I take this and store it inside of my linked list? I have looked at a few examples, but the class I was provided with for my class is a little different, and I haven't been able to find something to specifically fix my problem.
Controller Code:
public class RentGameDialogController extends RentalStoreGUIController implements Initializable{
/** TextField Objects **/
#FXML private TextField nameField, rentedOnField, dueBackField;
/** String for NameField **/
String name, rentedOn, dueBack;
/** Game ComboBox ID's **/
#FXML private ObservableList<GameType> cbGameOptions;
#FXML private ComboBox<GameType> cbGame;
/** Console ComboBox ID's **/
#FXML private ObservableList<PlayerType> cbConsoleOptions;
#FXML private ComboBox<PlayerType> cbConsole;
/** GameType object **/
private GameType game;
/** PlayerType Object **/
private PlayerType console;
/** Button ID's **/
#FXML Button cancel, addToCart;
/** Counter for calculating total **/
int gameCounter;
/** Stage for closing GUI **/
private Stage currentStage;
private MyLinkedList list = new MyLinkedList();
#Override
public void initialize(URL location, ResourceBundle resources) {
/** Select Console **/
cbConsoleOptions = FXCollections.observableArrayList();
for (PlayerType p : PlayerType.values()) { cbConsoleOptions.addAll(p); }
cbConsole.getItems().addAll(cbConsoleOptions);
/** Select Game **/
cbGameOptions = FXCollections.observableArrayList();
for (GameType g : GameType.values()){ cbGameOptions.addAll(g); }
cbGame.getItems().addAll(cbGameOptions);
}
public String getName(){
name = nameField.getText();
try {
String[] firstLast = name.split(" ");
String firstName = firstLast[0];
String lastName = firstLast[1];
} catch (Exception e){
e.printStackTrace();
}
return name;
}
public String getGame() {
return cbGame.getSelectionModel().getSelectedItem().toString();
}
public String getConsole() {
return cbConsole.getSelectionModel().getSelectedItem().toString();
}
public String getRentedOn() throws ParseException {
rentedOn = rentedOnField.getText();
DateFormat format = new SimpleDateFormat("dd/MM/yyyy");
Date rentedOnDate = format.parse(rentedOn);
Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(rentedOnDate);
try {
cal.getTime();
} catch (Exception e) {
rentedOnField.setText("ERROR");
}
return rentedOn;
}
public String getDueBack() throws ParseException {
dueBack = dueBackField.getText();
DateFormat format = new SimpleDateFormat("dd/MM/yyyy");
Date dueBackDate = format.parse(dueBack);
Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(dueBackDate);
try {
cal.getTime();
} catch (Exception e) {
dueBackField.setText("ERROR");
}
return dueBack;
}
/*************************************
* This is the method to call the other
* String methods so their output can be
* put into my main GUI
*
*
* #return
* #throws ParseException
*************************************/
public String storePurchaseData() throws ParseException {
gameCounter++;
String toList = getName() + " | " + getGame() + " | " + getConsole() + " | " +
getRentedOn() + " | " + getDueBack();
//Add 'toList' to the linked list here if possible
return toList;
}
#FXML
public void handleCancelButtonAction () {
currentStage = (Stage) cancel.getScene().getWindow();
currentStage.close();
}
#FXML
public void addToCartButton () throws ParseException {
appendTextArea(storePurchaseData());
currentStage = (Stage) cancel.getScene().getWindow();
currentStage.close();
}}
This code is for my controller. It launches a basic GUI, then I can pull the data from all of the fields I made, convert them to Strings and can then print them in one long chain of text. I would like to store the string into my linked list class.
Linked List code:
public class MyLinkedList<E> implements Serializable {
private DNode<E> top;
public int size;
public MyLinkedList() {
top = null;
size = 0;
}
}
I am very new to linked lists and I am trying to understand them, does this code make sense? Do I need to add anything to, say, save the String that I am storing into a text file?
Thank you in advance
Without getting into your game code at all, it looks like your MyLinkedList class takes a type parameter E - You haven't shown the code for DNode but it also takes the E type. If you can specify this to be a String then the nodes of MyLinkedList can be populated with Strings as you desire.
DNode<String> myFirstNode = new DNode<>(null, null, "nodeData");
MyLinkedList<String> list = new MyLinkedList<>(myFirstNode);
This assumes that the MyLinkedList class also has a constructor that takes a DNode to initialize its head, and that DNode looks something like this.