JavaFX: how to hide the item selection after having selected an item - javafx

I made a WebEngine where i can use favorite Web URLS from a Choicebox (= favBox).
After having chosen an item, the item is shown and the website is loading. But my problem is: the item can still be seen for the rest of the session. How can I hide the item selection and just show the ChoiceBox without items?
Thanks a lot
#FXML
private void handleFavoritLoading(MouseEvent event) {
//favBox is a ChoiceBox
favBox.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
System.out.println("OK");
browser.load(favBox.getItems().get(newValue.intValue()));
// how to make the favBox not showing the selected item???
}
});
}

You can clear the selection of a ChoiceBox, then nothing will be selected in it.
favBox.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue != null) {
browser.load(newValue);
favBox.getSelectionModel().clearSelection();
}
}
);
Note that this behavior is a little bit strange as most of the time you probably want the selected choice to continue to be shown after selection. However, if you don't want the standard operation and want to immediately clear the choice after selection, you can always use the sample code provided here.
Sample app:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.web.*;
import javafx.stage.Stage;
import static javafx.collections.FXCollections.observableArrayList;
public class HiddenChoices extends Application {
#Override
public void start(Stage stage) throws Exception {
WebView webView = new WebView();
WebEngine browser = webView.getEngine();
VBox.setVgrow(webView, Priority.ALWAYS);
ChoiceBox<String> favBox = new ChoiceBox<>(
observableArrayList(
"http://www.google.com",
"http://andrew-hoyer.com/experiments/cloth/",
"http://www.effectgames.com/demos/canvascycle/",
"http://www.zynaps.com/site/experiments/environment.html?mesh=bart.wft"
)
);
favBox.getSelectionModel().selectedItemProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue != null) {
browser.load(newValue);
favBox.getSelectionModel().clearSelection();
}
}
);
ProgressBar progress = new ProgressBar();
progress.progressProperty().bind(browser.getLoadWorker().progressProperty());
progress.visibleProperty().bind(browser.getLoadWorker().runningProperty());
HBox controls = new HBox(10, favBox, progress);
controls.setMinHeight(HBox.USE_PREF_SIZE);
controls.setAlignment(Pos.CENTER_LEFT);
stage.setScene(
new Scene(
new VBox(10, controls, webView)
)
);
stage.show();
favBox.getSelectionModel().select(0);
}
public static void main(String[] args) {
Application.launch();
}
}

Related

Drag and Drop an item from a treeview into a textArea

I would like to drag and drop an item from a treeview into a textArea.
The goal is to write in the textArea the item dropped.
So if i choose to drop the item b, I would like to have b written in the textArea.
I have two problems.
1°) I have an error
Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Only serializable objects or ByteBuffer can be used as data with data format []
2°) My setOn events don't run at all.
At any time, an event is detected.
package application;
import java.io.Serializable;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class testDragAndDropTreeView extends Application implements Serializable {
private final TreeView<String> tree = new TreeView<String>();
private final TextArea text = new TextArea();
private static final DataFormat DATAFORMAT = new DataFormat();
#Override
public void start(Stage Stage) {
HBox hbox = new HBox();
hbox.setPadding(new Insets(20));
TreeItem<String> a = new TreeItem<String>("a");
TreeItem<String> b = new TreeItem<String>("b");
TreeItem<String> c = new TreeItem<String>("c");
TreeItem<String> root = new TreeItem<String>("Root");
root.getChildren().addAll(a, b, c);
root.setExpanded(true);
tree.setRoot(root);
tree.setOnDragDetected(event -> {
Dragboard db = tree.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.put(DATAFORMAT, tree.getSelectionModel().getSelectedItem());
db.setContent(content);
});
text.setOnDragOver(event -> {
if (event.getGestureSource() != text && event.getDragboard().hasContent(DATAFORMAT)) {
event.acceptTransferModes(TransferMode.ANY);
}
event.consume();
});
text.setOnDragDropped(event -> {
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasContent(DATAFORMAT)) {
Person droppedPerson = (Person) db.getContent(DATAFORMAT);
text.appendText(droppedPerson.getName() + "\n");
success = true;
}
event.setDropCompleted(success);
event.consume();
});
tree.setCellFactory(param -> {
return new TreeCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
setStyle(null);
setGraphic(null);
setText(null);
return;
}
setText(item);
}
};
});
hbox.getChildren().addAll(tree, text);
Stage.setScene(new Scene(hbox));
Stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
A few of observations:
You were trying to put TreeItems in the clipboard content, but they aren't serializable and are not of the expected type, so they don't fit. You actually wanted to put the value of the tree items in the clipboard (I think).
You can just use a map to transfer data (clipboard content is also a map, so I guess you could still use that if you wanted).
You just want strings of data, so you should use DataFormat.PLAIN_TEXT and put a string in the transfer object.
You should cater for what to do if nothing is selected (selected item is null).
Your code was referencing a non-existent class Person, I just changed it to String.
The action of initiating a drag from a tree item selects the tree item before the drag occurs (this is the default operation of the tree with the mouse events you are consuming, not additional code).
The action of initiating a drag from a blank cell below the visible tree items, will drag and drop the selected item (if there is one or nothing at all), which is a bit strange but sounds like what you want to occur from comments.
An alternative would be to define drag handlers on the tree cells themselves rather than the entire tree, but that differs a bit from the design you seem to have.
Your code wasn't consistently following camel case naming conventions for Java, so I fixed some names. It is especially a bad idea to name a Stage variable Stage exactly the same case as the class name, as that is really confusing.
The root is shown and draggable. If you don't want the root shown, you can call tree.setShowRoot(false).
Example
Test this modification of your code and verify if it does what you want. If it is not exactly what you want, perhaps you can use it as a new starting point and tweak it how you want.
import java.io.Serializable;
import java.util.Map;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class DragAndDropTreeViewApp extends Application implements Serializable {
private static final DataFormat DATA_FORMAT = DataFormat.PLAIN_TEXT;
private final TreeView<String> tree = new TreeView<>();
private final TextArea text = new TextArea();
#Override
public void start(Stage stage) {
HBox hbox = new HBox();
hbox.setPadding(new Insets(20));
TreeItem<String> a = new TreeItem<>("a");
TreeItem<String> b = new TreeItem<>("b");
TreeItem<String> c = new TreeItem<>("c");
TreeItem<String> root = new TreeItem<>("Root");
//noinspection unchecked
root.getChildren().addAll(a, b, c);
root.setExpanded(true);
tree.setRoot(root);
tree.setOnDragDetected(event -> {
if (tree.getSelectionModel().getSelectedItem() != null) {
Dragboard db = tree.startDragAndDrop(TransferMode.ANY);
db.setContent(
Map.of(
DATA_FORMAT,
tree.getSelectionModel().getSelectedItem().getValue()
)
);
}
});
text.setOnDragOver(event -> {
if (event.getGestureSource() != text && event.getDragboard().hasContent(DATA_FORMAT)) {
event.acceptTransferModes(TransferMode.ANY);
}
event.consume();
});
text.setOnDragDropped(event -> {
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasContent(DATA_FORMAT)) {
String droppedPerson = (String) db.getContent(DATA_FORMAT);
text.appendText(droppedPerson + "\n");
success = true;
}
event.setDropCompleted(success);
event.consume();
});
tree.setCellFactory(param -> new TreeCell<>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setStyle(null);
setGraphic(null);
setText(null);
return;
}
setText(item);
}
});
hbox.getChildren().addAll(tree, text);
stage.setScene(new Scene(hbox));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Interacting with custom CellFactory node adds row to TableView selection list?

I have a TableView with a CellFactory that places a ComboBox into one of the columns. The TableView has SelectionMode.MULTIPLE enabled but it is acting odd with the ComboBox cell.
When the users clicks on the ComboBox to select a value, that row is added to the list of selected rows. Instead, clicking on the ComboBox should either select that row and deselect all others (unless CTRL is being held), or it should not select the row at all, but only allow for interaction with the ComboBox.
I am not sure how to achieve this.
Here is a complete example to demonstrate the issue:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
enum Manufacturer {
HP, DELL, LENOVO, ASUS, ACER;
}
public class TableViewSelectionIssue extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// Simple TableView
TableView<ComputerPart> tableView = new TableView<>();
TableColumn<ComputerPart, Manufacturer> colManufacturer = new TableColumn<>("Manufacturer");
TableColumn<ComputerPart, String> colItem = new TableColumn<>("Item");
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
colManufacturer.setCellValueFactory(t -> t.getValue().manufacturerProperty());
colItem.setCellValueFactory(t -> t.getValue().itemNameProperty());
tableView.getColumns().addAll(colManufacturer, colItem);
// CellFactory to display ComboBox in colManufacturer
colManufacturer.setCellFactory(param -> new ManufacturerTableCell(colManufacturer, FXCollections.observableArrayList(Manufacturer.values())));
// Add sample items
tableView.getItems().addAll(
new ComputerPart("Keyboard"),
new ComputerPart("Mouse"),
new ComputerPart("Monitor"),
new ComputerPart("Motherboard"),
new ComputerPart("Hard Drive")
);
root.getChildren().add(tableView);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
}
class ComputerPart {
private final ObjectProperty<Manufacturer> manufacturer = new SimpleObjectProperty<>();
private final StringProperty itemName = new SimpleStringProperty();
public ComputerPart(String itemName) {
this.itemName.set(itemName);
}
public Manufacturer getManufacturer() {
return manufacturer.get();
}
public void setManufacturer(Manufacturer manufacturer) {
this.manufacturer.set(manufacturer);
}
public ObjectProperty<Manufacturer> manufacturerProperty() {
return manufacturer;
}
public String getItemName() {
return itemName.get();
}
public void setItemName(String itemName) {
this.itemName.set(itemName);
}
public StringProperty itemNameProperty() {
return itemName;
}
}
class ManufacturerTableCell extends TableCell<ComputerPart, Manufacturer> {
private final ComboBox<Manufacturer> cboStatus;
ManufacturerTableCell(TableColumn<ComputerPart, Manufacturer> column, ObservableList<Manufacturer> items) {
this.cboStatus = new ComboBox<>();
this.cboStatus.setItems(items);
this.cboStatus.setConverter(new StringConverter<Manufacturer>() {
#Override
public String toString(Manufacturer object) {
return object.name();
}
#Override
public Manufacturer fromString(String string) {
return null;
}
});
this.cboStatus.disableProperty().bind(column.editableProperty().not());
this.cboStatus.setOnShowing(event -> {
final TableView<ComputerPart> tableView = getTableView();
tableView.getSelectionModel().select(getTableRow().getIndex());
tableView.edit(tableView.getSelectionModel().getSelectedIndex(), column);
});
this.cboStatus.valueProperty().addListener((observable, oldValue, newValue) -> {
if (isEditing()) {
commitEdit(newValue);
column.getTableView().refresh();
}
});
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(Manufacturer item, boolean empty) {
super.updateItem(item, empty);
setText(null);
if (empty) {
setGraphic(null);
} else {
this.cboStatus.setValue(item);
this.setGraphic(this.cboStatus);
}
}
}
The example begins with a predictable UI:
However, when interacting with the ComboBox in the Manufacturer column, the corresponding row is selected. This is expected for the first row, but it does not get deselected when interacting with another ComboBox.
How can I prevent subsequent interactions with a ComboBox from adding to the selected rows? It should behave like any other click on a TableRow, should it not?
I am using JDK 8u161.
Note: I understand there is a ComboBoxTableCell class available, but I've not been able to find any examples of how to use one properly; that is irrelevant to my question, though, unless the ComboBoxTableCell behaves differently.
Since you want an "always editing" cell, your implementation should behave more like CheckBoxTableCell than ComboBoxTableCell. The former bypasses the normal editing mechanism of the TableView. As a guess, I think it's your use of the normal editing mechanism that causes the selection issues—why exactly, I'm not sure.
Modifying your ManufactureTableCell to be more like CheckBoxTableCell, it'd look something like:
class ManufacturerTableCell extends TableCell<ComputerPart, Manufacturer> {
private final ComboBox<Manufacturer> cboStatus;
private final IntFunction<Property<Manufacturer>> extractor;
private Property<Manufacturer> property;
ManufacturerTableCell(IntFunction<Property<Manufacturer>> extractor, ObservableList<Manufacturer> items) {
this.extractor = extractor;
this.cboStatus = new ComboBox<>();
this.cboStatus.setItems(items);
// removed StringConverter for brevity (accidentally)
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
cboStatus.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
if (event.isShortcutDown()) {
getTableView().getSelectionModel().select(getIndex(), getTableColumn());
} else {
getTableView().getSelectionModel().clearAndSelect(getIndex(), getTableColumn());
}
event.consume();
});
}
#Override
protected void updateItem(Manufacturer item, boolean empty) {
super.updateItem(item, empty);
setText(null);
clearProperty();
if (empty) {
setGraphic(null);
} else {
property = extractor.apply(getIndex());
Bindings.bindBidirectional(cboStatus.valueProperty(), property);
setGraphic(cboStatus);
}
}
private void clearProperty() {
setGraphic(null);
if (property != null) {
Bindings.unbindBidirectional(cboStatus.valueProperty(), property);
}
}
}
And you'd install it like so:
// note you could probably share the same ObservableList between all cells
colManufacturer.setCellFactory(param ->
new ManufacturerTableCell(i -> tableView.getItems().get(i).manufacturerProperty(),
FXCollections.observableArrayList(Manufacturer.values())));
As already mentioned, the above implementation bypasses the normal editing mechanism; it ties the value of the ComboBox directly to the model item's property. The implementation also adds a MOUSE_PRESSED handler to the ComboBox that selects the row (or cell if using cell selection) as appropriate. Unfortunately, I'm not quite understanding how to implement selection when Shift is down so only "Press" and "Shortcut+Press" is handled.
The above works how I believe you want it to, but I could only test it out using JavaFX 12.

JavaFx Tableview checkbox requires focus

I implemented boolean representation in my tableView as checkbox. It works fine in general but very irritating fact is that it requires row to be focused (editing) to apply change of checkbox value. It means I first have to double click on the field and then click checkbox.
How to make checkbox change perform onEditCommit right away?
public class BooleanCell<T> extends TableCell<T, Boolean> {
private CheckBox checkBox;
public BooleanCell() {
checkBox = new CheckBox();
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (isEditing())
commitEdit(newValue == null ? false : newValue);
}
});
setAlignment(Pos.CENTER);
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
this.setEditable(true);
}
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
checkBox.setSelected(item);
setGraphic(checkBox);
}
}
}
I'm not sure about the rest of your implementation, but I assume you do not have your TableView set to editable:
tableView.setEditable(true);
On a side note, you could easily use a CheckBoxTableCell instead of implementing your own (BooleanCell).
Here is a very simple application you can run to see how this works. Each CheckBox may be clicked without first focusing the row and its value updates your underlying model as well (which you can see by clicking the "Print List" button).
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CheckBoxTableViewSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// List of sample items
ObservableList<MyItem> myItems = FXCollections.observableArrayList();
myItems.addAll(
new MyItem(false, "Item 1"),
new MyItem(false, "Item 2"),
new MyItem(true, "Item 3"),
new MyItem(false, "Item 4"),
new MyItem(false, "Item 5")
);
// Create TableView
TableView<MyItem> tableView = new TableView<MyItem>();
// We need the TableView to be editable in order to allow each CheckBox to be selectable
tableView.setEditable(true);
// Create our table Columns
TableColumn<MyItem, Boolean> colSelected = new TableColumn<>("Selected");
TableColumn<MyItem, String> colName = new TableColumn<>("Name");
// Bind the columns with our model's properties
colSelected.setCellValueFactory(f -> f.getValue().selectedProperty());
colName.setCellValueFactory(f -> f.getValue().nameProperty());
// Set the CellFactory to use a CheckBoxTableCell
colSelected.setCellFactory(param -> {
return new CheckBoxTableCell<MyItem, Boolean>();
});
// Add our columns to the TableView
tableView.getColumns().addAll(colSelected, colName);
// Set our items to the TableView
tableView.setItems(myItems);
// Create a button to print out our list of items
Button btnPrint = new Button("Print List");
btnPrint.setOnAction(event -> {
System.out.println("-------------");
for (MyItem item : myItems) {
System.out.println(item.getName() + " = " + item.isSelected());
}
});
root.getChildren().addAll(tableView, btnPrint);
// Show the Stage
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
/**
* Just a simple sample class to display in our TableView
*/
final class MyItem {
// This property will be bound to the CheckBoxTableCell
private final BooleanProperty selected = new SimpleBooleanProperty();
// The name of our Item
private final StringProperty name = new SimpleStringProperty();
public MyItem(boolean selected, String name) {
this.selected.setValue(selected);
this.name.set(name);
}
public boolean isSelected() {
return selected.get();
}
public BooleanProperty selectedProperty() {
return selected;
}
public void setSelected(boolean selected) {
this.selected.set(selected);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
}

JavaFX Dynamic Form Field UI

Does anyone know how to imitate the functionality from the UI components shown below? I want to replicate adding form fields when text is entered into the TextField box. I don't need the dropdown button, just the dynamic adding of the forms.
You could modify the children of a GridPane adding a new TextField & Button every time one of the buttons is activated. Listen to the text properties to enable/disable the Button and save the results.
private static void insertRow(GridPane grid, List<String> values, int index) {
// increment index of children with rowIndex >= index
for (Node n : grid.getChildren()) {
int row = GridPane.getRowIndex(n);
if (row >= index) {
GridPane.setRowIndex(n, row + 1);
}
}
TextField text = new TextField();
Button add = new Button("+");
add.setDisable(true);
add.setOnAction(evt -> {
insertRow(grid, values, GridPane.getRowIndex(add) + 1);
});
values.add(index, "");
text.textProperty().addListener((a, oldValue, newValue) -> {
add.setDisable(newValue.isEmpty());
values.set(GridPane.getRowIndex(add), newValue);
});
grid.addRow(index, text, add);
}
#Override
public void start(Stage primaryStage) throws Exception {
GridPane grid = new GridPane();
List<String> list = new ArrayList<>();
insertRow(grid, list, 0);
Button print = new Button("print");
print.setOnAction(evt -> {
System.out.println(list);
});
grid.add(print, 0, 1);
Scene scene = new Scene(grid, 300, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
This may not be exactly what you're looking for and may not be the best way to do this, but should be easy to adapt it to your needs.
Basically, you will need a list of HBox objects to be added to a VBox in your application. You could create the list yourself and bind it to the children of your VBox, or just add/remove the HBoxes to/from the VBox using the getChildren().add() and getChildren().remove() methods.
Here is a complete little application to demonstrate the concept. I created an internal class to handle the HBox with the fields you need. This could be adapted to be more felixable:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
private static VBox mainPane;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
mainPane = new VBox(5);
mainPane.setPadding(new Insets(10));
mainPane.setAlignment(Pos.TOP_CENTER);
mainPane.getChildren().add(new UIForms());
primaryStage.setScene(new Scene(mainPane));
primaryStage.show();
}
static void addField() {
mainPane.getChildren().add(new UIForms());
}
static void removeField(UIForms field) {
if (mainPane.getChildren().size() > 1) {
mainPane.getChildren().remove(field);
}
}
}
class UIForms extends HBox {
private TextField textField1;
private TextField textField2;
private Button btnAddField;
private Button btnRemoveField;
public UIForms() {
// Setup the HBox layout
setAlignment(Pos.CENTER_LEFT);
setSpacing(5);
// Create the UI controls
textField1 = new TextField();
textField2 = new TextField();
btnAddField = new Button("+");
btnRemoveField = new Button("-");
// Setup button actions
btnAddField.setOnAction(e -> Main.addField());
btnRemoveField.setOnAction(e -> Main.removeField(this));
// Add the UI controls
getChildren().addAll(
textField1, textField2, btnAddField, btnRemoveField
);
}
}

JavaFX datepicker - how to update date in a second datepicker object?

Problem:
I've two datepicker object checkIn_date and checkOut_date in the same scene.
There is a way to change automatically the date field in the second datepicker object?
For example : checkIn_date is set with 2015-08-10 and checkOut_date is set with 2015-08-11. If I change the date field in checkIn_date i.e. 2015-08-22, checkOut_date update automatically to 2015-08-23.
Thanks for any advices.
You can achieve this by adding a listener to your check-in DatePicker, fetch the new value, add the no of days you want to it and update the new value to the check-out DatePicker.
Here is a MCVE to give more idea on what I meant :
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
private final int noOfDaysToAdd = 2;
#Override
public void start(Stage primaryStage) throws Exception {
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
Label checkInLabel = new Label("Check In : ");
Label checkOutLabel = new Label("Check Out : ");
DatePicker picker1 = new DatePicker();
DatePicker picker2 = new DatePicker();
// Listener for updating the checkout date w.r.t check in date
picker1.valueProperty().addListener((ov, oldValue, newValue) -> {
picker2.setValue(newValue.plusDays(noOfDaysToAdd));
});
HBox checkInBox = new HBox(10, checkInLabel, picker1);
HBox checkOutBox = new HBox(10, checkOutLabel, picker2);
checkInBox.setAlignment(Pos.CENTER);
checkOutBox.setAlignment(Pos.CENTER);
root.getChildren().addAll(checkInBox, checkOutBox);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Output:
Update
You can re-write the snippet
picker1.valueProperty().addListener((ov, oldValue, newValue) -> {
picker2.setValue(newValue.plusDays(noOfDaysToAdd));
});
without the lambda as :
picker1.valueProperty().addListener(new ChangeListener<LocalDate>() {
#Override
public void changed(ObservableValue<? extends LocalDate> observable, LocalDate oldValue, LocalDate newValue) {
picker2.setValue(newValue.plusDays(noOfDaysToAdd));
}
});
First thanks for the help!
I've fixed my problem from another point of view.
When scene/view has been loaded either datepicker are set to current date for check-in and the day after the curent day for check-out date.
For check-in day I've disabled the selection of days before the current date and after check-out date.
For check-out day I've disabled the selection of days before check-out date.
Unfortunately I can't post any image because my reputation is not 10!
But this is the code for the controller class:
import java.time.LocalDate;
import javafx.fxml.FXML;
import javafx.scene.control.DatePicker;
import com.main.controller.checker.DateChecker;
import com.main.controller.datautil.DataFetch;
import com.main.controller.datautil.DataStore;
import com.main.controller.util.Initilizable;
public class SearchCtrl implements Initilizable{
#FXML
private DatePicker check_in;
#FXML
private DatePicker check_out;
#Override
public void init() {
check_in.setValue(LocalDate.now());
check_out.setValue(check_in.getValue().plusDays(1));
DateChecker.setBeginDateBounds(check_in, check_out.getValue());
DateChecker.setEndDateBounds(check_out, check_in.getValue());
check_in.setOnAction( (event) -> {DateChecker.setEndDateBounds(check_out, check_in.getValue());});
datafine.setOnAction( (event) -> {DateChecker.setBeginDateBounds(check_in, check_out.getValue());});
}
This is the DateChecker class:
import java.time.LocalDate;
import javafx.scene.control.DateCell;
import javafx.scene.control.DatePicker;
import javafx.util.Callback;
public class DateChecker {
private DateChecker(){
}
public static void setBeginDateBounds(DatePicker begin_date, LocalDate end_date ){
final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
#Override
public DateCell call(final DatePicker datePicker) {
return new DateCell() {
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
boolean cond = (item.isBefore(LocalDate.now()) || !item.isBefore(end_date));
if (cond){
setDisable(true);
setStyle("-fx-background-color: #d3d3d3;");
}else{
setDisable(false);
setStyle("-fx-background-color: #CCFFFF;");
setStyle("-fx-font-fill: black;");
}
}
};
}
};
begin_date.setDayCellFactory(dayCellFactory);
}
public static void setEndDateBounds(DatePicker end_date, LocalDate begin_date ){
final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
#Override
public DateCell call(final DatePicker datePicker) {
return new DateCell() {
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
boolean cond = (item.isBefore(LocalDate.now()) || !item.isAfter(begin_date));
if (cond){
setDisable(true);
setStyle("-fx-background-color: #d3d3d3;");
}else{
setDisable(false);
setStyle("-fx-background-color: #CCFFFF;");
setStyle("-fx-font-fill: black;");
}
}
};
}
};
end_date.setDayCellFactory(dayCellFactory);
}
}
I hope this will help!

Resources