Instantiating ObservableIntegerValue in javafx - javafx

I have a variable ObservableIntegerValue called score .
I wanted to create a listener for it to listen for the changes of its value and according to that change the javafx label text displayed on my pane.
But in the method initialize() i need to instantiate and give it the initial value of 500 let's say.
How could it be done?

Beside an observable value, to make the score variable also bindable, you can use IntegerProperty instead of ObservableIntegerValue. IntegerProperty is an IntegerExpression, so it also implements ObservableIntegerValue interface, where IntegerExpression is,
A IntegerExpression is a ObservableIntegerValue plus additional
convenience methods to generate bindings in a fluent style.
IntegerProperty score = new SimpleIntegerProperty(500);
Text text = new Text("-");
// Bind score to text, to show on scene.
text.textProperty().bind(score.asString());
score.set(700); // new value
and the listener
score.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
// value changed
}
});

Related

Selecting multiple objects and displaying selection in JavaFX TableView

I am trying to display a scene with a TableView where a user can click to select teams, which are then stored in an ObservableList. The selected teams should be highlighted and clicking a selected team should unselect them. Selecting multiple teams should not require holding down ctrl etc. The problem that I'm having is actually displaying the selected teams. I'm very confused with JavaFX's SelectionModel and FocusModel. I'd also need a functionality where if the user opens the view again to make changes to the selection, the selected teams should appear highlighted again.
Here's a sample of the code I've been working with. So far the selection works but the UI doesn't reflect the actual selection. In the image [1] is shown what I'm trying to achieve, the selection has been done holding ctrl and clicking the items. Again that doesn't reflect the actual selection and having to hold down keys to select multiple items is not viable in this case.
//for the sake of simplicity the Team objects are set here as a placeholder instead of fetching from the database
private Team[] teams = new Team[] {new Team("team1", 0), new Team("team2", 1), new Team("team3", 2), new Team("team4", 3)
private ObservableList<Team> selectedTeams = FXCollections.observableArrayList();
#FXML
private TableView<Team> teamsTableView;
#FXML
private TableColumn<Team, String> teamNameColumn;
#FXML
private TableColumn<Team, Integer> teamIdColumn;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
teamNameColumn.setCellValueFactory(new PropertyValueFactory<Team, String>("teamName"));
teamIdColumn.setCellValueFactory(new PropertyValueFactory<Team, Integer>("id"));
teamsTableView.setItems(teams);
teamsTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
teamsTableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Team>() {
#Override
public void changed(ObservableValue<? extends Team> observable, Team oldValue, Team newValue) {
if(selectedTeams.contains(newValue)) {
selectedTeams.remove(newValue);
} else {
selectedTeams.add(newValue);
}
}
});
}
What approach should I try to take? Could the SelectionModel reference an ObservableList somehow?
I've been working on this for over two weeks now and I'm completely stumped. This is clearly beyond my abilities and some hand-holding might be necessary.

JavaFX: Run ChangeListener

Ho can I run the change method inside ChangeListener in the initialization.
Since it only run when changes happen. For example I have a TextField that I want to set it's value to 0 when it's empty, and I do it like this:
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
if (newValue.isEmpty())
textField.setText("0");
}
});
But at the start of the application the changed method is not called. So how to work around this issue?
There's no way to access a listener added to a property. Obviously you could just keep a reference to it:
ChangeListener<String> textFieldListener = (observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
if (newValue.isEmpty())
textField.setText("0");
};
textField.textProperty().addListener(textFieldListener);
textFieldListener.changed(null, null, textField.getText());
or, perhaps more naturally, just move the actual functionality to a different method:
textField.textProperty().addListener((observable, oldValue, newValue) -> vetoTextFieldChanges());
vetoTextFieldChanges();
// ...
private void vetoTextFieldChanges() {
String newText = textField.getText();
if (!newText.matches("\\d*")) {
textField.setText(newText.replaceAll("[^\\d]", ""));
}
if (newText.isEmpty())
textField.setText("0");
}
Note that the whole approach of watching for changes and then modifying them if they are inconsistent with your business logic is not very satisfactory. For example, if you have other listeners registered with the property, they may see the intermediate (invalid) values of the text. The supported way to do this is to use a TextFormatter. The TextFormatter both allows you to veto changes that are requested to the text, before the textProperty() is updated, and to convert the text to an appropriate value. So in your case you could do:
UnaryOperator<TextFormatter.Change> filter = change -> {
// remove any non-digit characters from inserted text:
if (! change.getText().matches("\\d*")) {
change.setText(change.getText().replaceAll("[^\\d]", ""));
}
// if new text is empty, replace all text with "0":
if (change.getControlNewText().isEmpty()) {
change.setRange(0, change.getControlText().length());
change.setText("0");
}
return change ;
};
TextFormatter<Integer> formatter = new TextFormatter<Integer>(new IntegerStringConverter(), 0, filter);
textField.setTextFormatter(formatter);
Now you can use the formatter to get the (numeric) values directly via the formatter's value property, and bind them to your model, etc:
// assume model is a data model and valueProperty() returns a Property<Integer>:
formatter.valueProperty().bindBidirectional(model.valueProperty());
Note that a binding like this will both update the formatter (and consequently the text field) when the model changes, and will also initialize it depending on your model value (thus providing a solution for your original question).

How do I automatically trigger enter key in Javafx

Nowadays I am working on raspberry pi and I write some programs in java , javafx platforms.I just would like to inform you that I am simply beginner on javafx.
According to that I just would like to trigger ENTER key after changing my textfield.Working principle of my program is like this;
1)I have created one masterform fxml and it is directing all other pages with one textfield.
2)I created main method that let me to use keyboard to enter some specific String values to assign them to textfield for page alteration.
3)I have a bridge java page, it includes global variables to use everywhere in project.So Firstly I set value from keyboard to these global variables.These global variables are created as stringproperty for adding actionlistener for any change.
4)Then I set these global variables to textfield.
5)Textfield indicates relevant values from keyboard.But Unfortunately I can not forward the pages without pressing to enter key.In this case ı would like to trigger this textfield.But unfortunately ı have no idea how to trigger texfield without pressing enter key.Therefore I decided to make auto trigger to enter key for this textfield.
I simply used robot method;
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_ENTER);
But it didn't work.Because After I set the global variable to textfield for first time.It does not define the value of the textfield is changed.It determines after pressing the enter key.
So how can I trigger this textfield after getting value of my global variables.I would like to pass how to set pages, I will show you how my program works.
Example of my code is;
Main method
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
for (String strBarcode = scanner.nextLine(); !strBarcode.isEmpty();
strBarcode = scanner.nextLine()) {
if (strBarcode.equals("distribution")){
Global.G_MOD.set("distribution");
System.out.println(Global.G_MOD.get());
}
}}
GlobalVariables.java(bridge page)
public class Global{
public static StringProperty G_MOD = new SimpleStringProperty("");
}
My MasterController Page for javafx
public class masterformController implements Initializable {
#FXML
public TextField tbxBarcode;
#FXML
void onchangetbxBarcode(ActionEvent event) {
if(Global.G_MOD.get().equals("distribution")){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/puttolightfx/fxml/page1.fxml"));
Parent rootpage1 = (Parent)loader.load();
pnPages.getChildren().clear();
pnPages.getChildren().add(rootpage1);
} catch (IOException ex) {
Logger.getLogger(masterformController.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
Global.G_MOD.addListener(new ChangeListener(){
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
String Newvalue = (String)newValue;
tbxBarcode.setText(Global.G_MOD.get());}
});
}
}
So Everything is working, just I have to trigger textfield when the global value : Global.G_MOD is indicated on texfield.Then it will pass to another page according to global value of Global.G_MOD : "distribution".
SOLUTION(SOLVED):
I solved my problem using thread on listener of the textfield.I gave up to trigger enter key automatically and focused on textfield change.
I simply decided to use thread to change .fxml pages in textfield listener.
Platform.runLater(new Runnable() {
#Override
public void run() {
//if you change the UI, do it here !
}
});
EDITED CODE :
tbxBarcode.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
String Newvalue=(String)newValue;
System.out.println(tbxBarcode.getText());
Platform.runLater(new Runnable() {
#Override
public void run() {
if(Global.G_MOD.get().equals("distribution")){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/puttolightfx/fxml/page1.fxml"));
Parent rootpage1 = (Parent)loader.load();
pnPages.getChildren().clear();
pnPages.getChildren().add(rootpage1);
} catch (IOException ex) {
Logger.getLogger(masterformController.class.getName()).log(Level.SEVERE, null, ex);
}
}
// }
}
});
});
Try using
textField.fireEvent(new KeyEvent(KeyEvent.KEY_PRESSED, "", "", KeyCode.ENTER, true, true, true, true));
According to the docs
public KeyEvent(EventType<KeyEvent> eventType,
String character,
String text,
KeyCode code,
boolean shiftDown,
boolean controlDown,
boolean altDown,
boolean metaDown)
Constructs new KeyEvent event with null source and target and KeyCode object directly specified.
Parameters:
eventType - The type of the event.
character - The character or sequence of characters associated with the event
text - A String describing the key code
code - The integer key code
shiftDown - true if shift modifier was pressed.
controlDown - true if control modifier was pressed.
altDown - true if alt modifier was pressed.
metaDown - true if meta modifier was pressed.
Since:
JavaFX 8.0
You can refer https://docs.oracle.com/javase/8/javafx/api/javafx/scene/input/KeyEvent.html
Edit 1
You need to identify the moment when Enter key event must be triggered.
For example:
If your textfield allows a limited number of characters, then you can add the above mentioned code in the following way:
txtField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue.length()>30) {
txtField.setText(oldValue);
txtField.fireEvent(new KeyEvent(KeyEvent.KEY_PRESSED, "", "", KeyCode.ENTER, true, true, true, true));
}
}
});
This is just an example. It can fire your event multiple times, so you need to write the code to fire the event just once.

ChoiceBox inside a TableView's cell only through custom Callback

I have been using a custom cell factory to display a ChoiceBox inside a TableView's TableColumn's cell, i.e.
someColumn.setCellFactory(new DayCellFactory());
with DayCellFactory implementing Callback (with the call and updateItem methods), and the ChoiceBox's (and thus the cell's) value being bound to the value of a property. The ChoiceBox shows the potential values of the enum Day.
class DayCellFactory implements Callback<TableColumn<DayPeriod, Day>, TableCell<DayPeriod, Day>> {
private final static ObservableList<Day> list = FXCollections.observableArrayList();
static {
for (Day day : Day.values()) {
list.add(day);
}
}
#Override
public TableCell<DayPeriod, Day> call (TableColumn<DayPeriod, Day> column) {
return new TableCell<DayPeriod, Day>() {
#Override
public void updateItem (Day item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
ObservableValue<Day> obsVal = getTableColumn().getCellObservableValue(getIndex());
ObjectProperty<Day> objProp = (ObjectProperty<Day>) obsVal;
ChoiceBox<Day> choice = new ChoiceBox<>(list);
choice.valueProperty().bindBidirectional(objProp);
setGraphic(choice);
}
}
};
}
}
I thought I could reduce the code by simply using
someColumn.setCellFactory(ChoiceBoxTableCell.forTableColumn(list));
(with the list defined and filled as required) but the resulting cell looks (and behaves) like a simple label showing the property's toString() value - yes, I did click there, but no ChoiceBox appears. And removing this line makes no difference. Removing the cell value factory results in no value being displayed.
I guess that there is no binding taking place with this simple approach, but this can probably be taken care of as well. It just seems strange that there is no ChoiceBox appearing.

JFXtras - How to add Change Listener to CalendarTextField?

how can I listen to a change in the CalendarTextField in JFXtras? For example a new choosen date from the picker-menu or a typed-in date?
date.addEventFilter(MouseEvent.ANY, new EventHandler<Event>() {
#Override
public void handle(Event arg0) {
System.out.println("EVENT");
}
});
gives me every movement of the mouse within the field. I didn't find another eventType that makes sense.
I also thought about adding this event filter in the window and check the selected date at every click in the window. But that can't be the right way.
Alright, found it here:
date.valueProperty().addListener(new ChangeListener<Calendar>() {
#Override
public void changed(
ObservableValue<? extends Calendar> observableValue,
Calendar oldValue, Calendar newValue) {
System.out.println(oldValue + " -> " + newValue);
}
});
I didn't realize that in FX a listener has to be set to the property, not to the component like in Swing.
I have redesigned a calendar component in a control with FXML, for the picker-menu I use the XCalendarPicker and I add a changelistener to it calendar() (who is a property). And when a date is changed I update my textfield with the new Date
final XCalendarPicker calendarPicker = new XCalendarPicker();
final ChangeListener<Calendar> calendarListener = new ChangeListener<Calendar>() {
#Override
public void changed(ObservableValue<? extends Calendar> observable, Calendar oldValue, Calendar newValue) {
Date date = newValue.getTime();
setDate(date);
}
};
calendarPicker.calendar().addListener(calendarListener);

Resources