I am working on a javafx project and I have included a DatePicker. I want to make the user only be able to select Mondays
DatePicker startDate = new DatePicker();
Any Ideas if it's possible?
There are two things you need to do:
Veto the change if the user changes the date in the text field to a date that is not a Monday. You can do this with the converter property of the DatePicker.
Disable all the cells in the popup that are not Mondays. You can do this with the dayCellFactory.
Simple example:
import java.time.DayOfWeek;
import java.time.LocalDate;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.DateCell;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.StringConverter;
public class DatePickerMondaysOnly extends Application {
#Override
public void start(Stage primaryStage) {
DatePicker datePicker = new DatePicker();
StringConverter<LocalDate> defaultConverter = datePicker.getConverter();
datePicker.setConverter(new StringConverter<LocalDate>() {
#Override
public String toString(LocalDate object) {
return defaultConverter.toString(object);
}
#Override
public LocalDate fromString(String string) {
LocalDate date = defaultConverter.fromString(string);
if (date.getDayOfWeek() == DayOfWeek.MONDAY) {
return date ;
} else {
// not a Monday. Revert to previous value.
// You could also, e.g., return the closest Monday here.
return datePicker.getValue();
}
}
});
datePicker.setDayCellFactory(dp -> new DateCell() {
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
setDisable(empty || item.getDayOfWeek() != DayOfWeek.MONDAY);
}
});
// Just for debugging: make sure we only see Mondays:
datePicker.valueProperty().addListener((obs, oldDate, newDate) -> {
if (newDate.getDayOfWeek() != DayOfWeek.MONDAY) {
System.out.println("WARNING: date chosen was not a Monday");
}
System.out.println(newDate + " (" + newDate.getDayOfWeek()+")");
});
StackPane root = new StackPane(datePicker);
root.setPadding(new Insets(20));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
I have a simple ComboBox that I've used ControlsFX to make auto-completing. This works perfectly except for one annoying flaw: When the user uses the dropdown to select an item with the mouse, the autocomplete window opens up, essentially offering matching suggestions to the user.
The desired behavior is for the selection of a value with the mouse to close the popup altogether.
The code below will demonstrate:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import org.controlsfx.control.textfield.TextFields;
public class AutoCompleteComboBox 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));
// Create a standard ComboBox
ComboBox<Person> comboBox = new ComboBox<>();
// Sample items
ObservableList<Person> people = FXCollections.observableArrayList();
people.addAll(
new Person("William", 34),
new Person("Ashley", 12),
new Person("Marvin", 63),
new Person("Suresh", 18),
new Person("Jackson", 24)
);
comboBox.setItems(people);
// Add StringConverter
comboBox.setConverter(new StringConverter<Person>() {
#Override
public String toString(Person person) {
return (person == null ? null : person.getName());
}
#Override
public Person fromString(String string) {
for (Person person : comboBox.getItems()) {
if (person.getName().equalsIgnoreCase(string)) {
return person;
}
}
return null;
}
});
// Make the ComboBox autocomplete using ControlsFX
comboBox.setEditable(true);
TextFields.bindAutoCompletion(comboBox.getEditor(), comboBox.getItems());
root.getChildren().add(comboBox);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
}
class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
#Override
public String toString() {
return name;
}
}
So typing into the editor and selecting a value with the keyboard works fine. But if you click the arrow to open the dropdown and select a value that way, you can see the issue (it essentially forces the user to select a value twice).
How would I go about preventing this behavior?
I am using JFoenix in my new JavaFX Application. I've successfully created my own message dialog.
package Dialog;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog;
import com.jfoenix.controls.JFXDialogLayout;
import com.jfoenix.controls.events.JFXDialogEvent;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
public class OK_Message extends JFXDialog {
private StackPane Container;
private JFXDialogLayout Content;
String DialogText;
String Headline;
JFXButton btn;
boolean OK = false;
private BooleanProperty okval;
public OK_Message(
StackPane Container,
String Headline,
String DialogText
){
this.Container = Container;
this.DialogText = DialogText;
this.Headline = Headline;
}
public void ShowDialog(){
setDialogContainer(Container);
setContent(getDialogContent());
setTransitionType(JFXDialog.DialogTransition.TOP);
setOverlayClose(false);
setFocusTraversable(true);
setOnDialogOpened((JFXDialogEvent event) -> {
Platform.runLater(()->{
btn.requestFocus();
});});
setOnKeyPressed((KeyEvent e)->{
e.consume();
if(e.getCode()== KeyCode.ENTER){close();}
else if(e.getCode()== KeyCode.SPACE){btn.requestFocus();}
else{}
});
show();
}
private JFXDialogLayout getDialogContent(){
Content = new JFXDialogLayout();
Content.setHeading(HeadLine());
Content.setBody(Body());
// Platform.runLater(()->{b.requestFocus();});
Content.setActions(getButton());
return Content;
}
private Label HeadLine(){
Label l = new Label(Headline);
l.setFont(new Font(18));
return l;
}
private GridPane Body(){
Label l = new Label(DialogText);
l.setFont(new Font(14));
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(8);
GridPane.setConstraints(l, 0, 0, 1, 1, HPos.LEFT, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS, new Insets(0, 0, 0, 0));
grid.getChildren().addAll(l);
return grid;
}
private JFXButton getButton(){
btn = new JFXButton("OK");
btn.setButtonType(JFXButton.ButtonType.FLAT);
btn.setPrefWidth(50);
btn.setTextFill(Color.WHITE);
btn.setOnAction((ActionEvent event) -> {
CloseDialog();
});
btn.setStyle("-fx-background-color:#FFFFFF");
btn.focusedProperty().addListener(new ChangeListener<Boolean>(){
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(newValue){
btn.setStyle("-fx-background-color:red");
}
}
});
return btn;
}
private void CloseDialog(){
close();
}
}
Now I can call my dialog anywhere: like this:
Dialog.OK_Message message =
new Dialog.OK_Message((StackPane)app_setup.getParent(),
"Message",
"Changes will be affected after restart.");
message.ShowDialog();
I cannot find a way to detect if use has clicked the button or not. Or what key is pressed by the user. If the user has clicked the OK Button I want to do something. Or if the use has pressed some key I want to do some other thing ? How can I listen to the above dialog ?
You can use setOnKeyPressed on your OK_Message class just like you used inside your class since it is extending the JFXDialog.
message.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println(event.getCode().getName());
}
});
For listening which button is clicked why not create customEventListenerfor your OK_Message class?
In your OK_Message.java file create an interface outside of the class like;
interface OnEventListener {
void onButtonCliked(String id);
}
and add a new method to your OK_Message class as
public void setOnEventListener(OnEventListener listener) {
mOnEventListener = listener;
}
First
Lets change your getButton() method to this one to add functionality of creating multiple buttons with different IDs and texts.
private JFXButton getButton(String id,String text){
JFXButton btn = new JFXButton(text);
btn.setId(id);
btn.setButtonType(JFXButton.ButtonType.FLAT);
btn.setPrefWidth(50);
btn.setTextFill(Color.WHITE);
btn.setOnAction((ActionEvent event) -> {
mOnEventListener.onButtonCliked(btn.getId()); //Here we are firing the event
CloseDialog();
});
btn.setStyle("-fx-background-color:red");
return btn;
}
In getDialogContent() lets create more than one button.
Content.setActions(getButton("Button1","OK"),getButton("Button2","CANCEL"));
Now you can use listeners after declaring new OK_Message objects
OK_Message message =
new OK_Message((StackPane)stackpane,
"Message",
"Changes will be affected after restart.");
message.ShowDialog();
message.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
System.out.println(event.getCode().getName());
}
});
message.setOnEventListener(new OnEventListener() {
#Override
public void onButtonCliked(String id) {
System.out.println(id);
}
});
Determine what do to according to the id observed by onButtonClicked() function.
I have a int value which I want to use for configuration. It can have 2 values - 0 for active and 1 for Blocked. I want to display this into friendly combo box:
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class MainApp extends Application
{
#Override
public void start(Stage stage) throws Exception
{
int state = 0;
ObservableList<String> options = FXCollections.observableArrayList(
"Active",
"Blocked"
);
ComboBox comboBox = new ComboBox(options);
BorderPane bp = new BorderPane(comboBox);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
It's not clear for me how I have to implement this into JavaFX Combobox.
When I have 0 I want to display this as Active and when I have 1 I want to display Blocked and also when I change the ComboBox value to update also int state value.
There are different ways to solve this problem. I have listed three of the solutions below. You can use any one of the below solutions which you feel is apt for your scenario.
Using a custom class
Create a custom class KeyValuePair, for storing the string and its corresponding value. Exposed the getters for the required fields.
Later, I have used the setCellFactory() of the comboxbox to show the required data. Use StringConverter to show the key in place of the object.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception
{
KeyValuePair keyValuePair1 = new KeyValuePair("Active", 0);
KeyValuePair keyValuePair2 = new KeyValuePair("Blocked", 1);
ObservableList<KeyValuePair> options = FXCollections.observableArrayList();
options.addAll(keyValuePair1, keyValuePair2);
ComboBox<KeyValuePair> comboBox = new ComboBox<>(options);
// show the correct text
comboBox.setCellFactory((ListView<KeyValuePair> param) -> {
final ListCell<KeyValuePair> cell = new ListCell<KeyValuePair>(){
#Override
protected void updateItem(KeyValuePair t, boolean bln) {
super.updateItem(t, bln);
if(t != null){
setText(String.valueOf(t.getKey()));
}else{
setText(null);
}
}
};
return cell;
});
comboBox.setConverter(new StringConverter<KeyValuePair>() {
#Override
public String toString(KeyValuePair object) {
return object.getKey();
}
#Override
public KeyValuePair fromString(String string) {
return null; // No conversion fromString needed.
}
});
// print the value
comboBox.valueProperty().addListener((ov, oldVal, newVal) -> {
System.out.println(newVal.getKey() + " - " + newVal.getValue());
});
BorderPane bp = new BorderPane(comboBox);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
class KeyValuePair {
private final String key;
private final int value;
public KeyValuePair(String key, int value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public int getValue() {
return value;
}
}
}
Without using an extra class
As suggested by #kleopatra, you can even do this without using an extra class.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
ObservableList<Integer> options = FXCollections.observableArrayList();
options.addAll(1, 0);
ComboBox<Integer> comboBox = new ComboBox<>(options);
// show the correct text
comboBox.setCellFactory((ListView<Integer> param) -> {
final ListCell<Integer> cell = new ListCell<Integer>(){
#Override
protected void updateItem(Integer t, boolean bln) {
super.updateItem(t, bln);
if(t != null){
setText(t == 1 ? "Active" : "Blocked");
}else{
setText(null);
}
}
};
return cell;
});
comboBox.setConverter(new StringConverter<Integer>() {
#Override
public String toString(Integer object) {
return object == 1 ? "Active" : "Blocked";
}
#Override
public Integer fromString(String string) {
return null;
}
});
// print the value
comboBox.valueProperty().addListener((ov, oldVal, newVal) -> {
System.out.println("Changed from " + oldVal + " to " + newVal);
});
BorderPane bp = new BorderPane(comboBox);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Using Bindings
You can also use Bindings if you don't want to take the pain of creating a new class and you will always have two elements i.e. Active and Blocked.
Just bind the valueProperty() of your combobox to the state, which is supposed to store the value i.e. 0 or 1.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
IntegerProperty state = new SimpleIntegerProperty();
ObservableList options = FXCollections.observableArrayList("Active", "Blocked");
ComboBox<String> comboBox = new ComboBox<>(options);
state.bind(Bindings.when(comboBox.valueProperty().isEqualTo("Active")).then(0).otherwise(1));
BorderPane bp = new BorderPane(comboBox);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is another solution:
declare state as BooleanProperty:
private BooleanProperty state = new SimpleBooleanProperty(false);
bind state property to the valueProperty of comboBox:
comboBox.valueProperty().bind(new When(state).then("Active").otherwise("Blocked"));
complete example:
public class ComboboxTest extends Application {
private BooleanProperty state = new SimpleBooleanProperty(false);
private Button button;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
ObservableList<String> options = FXCollections.observableArrayList(
"Active",
"Blocked"
);
ComboBox comboBox = new ComboBox(options);
button = new Button("false");
button.setOnAction(e -> setSate());
button.textProperty().bind(state.asString());
BorderPane bp = new BorderPane(comboBox);
StackPane stackpane = new StackPane(button);
stackpane.setAlignment(Pos.CENTER);
bp.setTop(stackpane);
bp.setPrefSize(800, 800);
Scene scene = new Scene(bp);
stage.setScene(scene);
stage.show();
comboBox.valueProperty().bind(new When(state).then("Active").otherwise("Blocked"));
}
public void setSate() {
if (state.get()) {
state.set(false);
} else {
state.set(true);
}
}
}
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!
I have this example of Date Picker. Can you tell me how I can set the date format of the calendar?
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.DatePicker;
import javafx.stage.Stage;
import java.time.LocalDate;
public class PickerDemo extends Application {
#Override public void start(Stage stage) {
final DatePicker datePicker = new DatePicker(LocalDate.now());
datePicker.setOnAction(event -> {
LocalDate date = datePicker.getValue();
System.out.println("Selected date: " + date);
});
stage.setScene(
new Scene(datePicker)
);
stage.show();
}
public static void main(String[] args) { launch(args); }
}
This should do the trick:
datePicker.setConverter(new StringConverter<LocalDate>()
{
private DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("dd/MM/yyyy");
#Override
public String toString(LocalDate localDate)
{
if(localDate==null)
return "";
return dateTimeFormatter.format(localDate);
}
#Override
public LocalDate fromString(String dateString)
{
if(dateString==null || dateString.trim().isEmpty())
{
return null;
}
return LocalDate.parse(dateString,dateTimeFormatter);
}
});