process EventHandler from getOnKeyPressed - java

I have element in fxml and i set method at onKeyPressed there
<TableView fx:id="topTable" onKeyPressed="#copyToClipboard" prefHeight="200.0" prefWidth="200.0">
but i don't understand how to get KeyCode's from this EventHandler in method.
#FXML
private TableView<ObservableList<String>> topTable;
...
public void copyToClipboard(){
System.out.println(topTable.getOnKeyPressed().toString());
}
With help of this action i would like to copy data from cells of TableColumn.
Can someone explain me what can i do with Handler from getOnKeyPressed?

When you add an event handler via FXML the method in the controller can either take no parameters or one parameter with the appropriate event type. In your case, since you're using onKeyPressed, you can define the controller method like so:
public void copyToClipboard(KeyEvent event) {
if (event.isShortcutDown() && event.getCode() == KeyCode.C) {
Clipboard cp = Clipboard.getSystemClipboard();
// add your data to the clipboard
}
}
For more information:
Javadoc of Clipboard
Documentation of controller event handler methods
To know which Event type the parameter should be, look at the event handler property you're attempting to use. In your question you are setting the onKeyPressed property via FXML. This property has the following signature:
ObjectProperty<EventHandler<? super KeyEvent>>
The type of Event the EventHandler is supposed to handle is stated in the generic type of the EventHandler; in this case, KeyEvent.
There are many of these "event handler properties" declared for Node. Some subclasses will add their own—such as the onAction property of ButtonBase (uses an ActionEvent).
If it helps, you can think of setting event handler properties from FXML as similar to using method references*:
public class Controller {
#FXML private TableView<?> topTable;
#FXML
private void initialize() {
topTable.setOnKeyPressed(this::copyToClipboard);
}
public void copyToClipboard(KeyEvent event) {}
}
* This is not actually the case as what the FXMLLoader does is more complicated (reflection).

Related

javafx choicebox to trigger a method onchange

Okay so i am very rusty on my java and even more on javafx. so i got a choicebox "categoryDrop" that when the value of the choicebox change i want to trigger this event that then takes the value of the choicebox and compare to an object "Folder" categorylist wich is an attribute it has.
here is my code
#FXML
private void folderByCategory(ActionEvent event) {
System.out.println("här1");
TreeItem<DocumentObject<?>> treeRoot = new TreeItem<>(new Folder());
for (Folder folder : logic.getFolderList()) {
if(f.getCategoryList().contains(categoryDrop.valueProperty())){
System.out.println("här2");
TreeItem<DocumentObject<?>> newFolders = new TreeItem<>(folder);
for(FileReference file : folder.getFileList()){
System.out.println(file.getName());
TreeItem<DocumentObject<?>> fileNode = new TreeItem<>(file);
newFolders.getChildren().add(fileNode);
}
treeRoot.getChildren().add(newFolders);
treeRoot.setExpanded(true);
}
treeNav.setRoot(treeRoot);
}
}
But then when i looked in scenebuilder i didnt see any good way to implement the method so it triggers when it changes. Anyone know a better way to do this? should i use a listener instead maybe?
ChoiceBox has an onAction property, so in FXML you can simply assign this controller method to this property:
<ChoiceBox fx:id="categoryDrop" onAction="#folderByCategory" />
Unfortunately, the current version of Scene Builder does not support this property, so you cannot set this directly from Scene Builder. There is a current issue filed for this.
Some workarounds are:
Edit the FXML manually to add the onAction attribute, as above.
Use a ComboBox instead of a ChoiceBox. The functionality is similar (though not identical) and a ComboBox will likely do what you need. Scene Builder does support the onAction property of a ComboBox.
Register the handler in the controller's initialize() method instead. All you need is
#FXML
private ChoiceBox<...> categoryDrop ;
public void initialize() {
categoryDrop.setOnAction(this::folderByCategory);
// existing code ...
}
#FXML
private void folderByCategory(ActionEvent event) {
// existing code...
}

Javafx how to access controls of second controller from first controller

I have one question regarding Javafx controller.
Lets say, I have multiple fxml files that are bind together in a main app. Then I have separate controllers for every fxml files. Lets see the following structure
com.par.app
- MainApp.java -> This is the main Application
- FirstController.java
- SecondController.java
com.par.app.view
- First.fxml
- Second.fxml
com.par.app.model
- MyModel -> This has some getter and setter methods.
Now as per above structure, I have a checkbox in First.fxml and a label in Second.fxml.
My Question : How can i set the label text in Second.FXML by checking and unchecking the checkbox in First.FXML , I have tried like this:
// In FirstController.Java
1) Initialize the SecondController
2) Get checkbox from FXMl as , priate CheckBox box1;
3) On initialize(....) method, I have set the event handler, as box1.setOnAction(enableHandle)
4) Finally the event Handler as,
EventHandler<ActionEvent> enableHandle = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (box1.isSelected()) {
secondController.setLabelText("PoP");
} else {
secondController.setText("Gone Wrong");
}
}
};
Similarly, On my second controller I have declared FXML control as,
#FXML
private Label lblTes;
// the method
public void setLabelText(String a)
{
this.lblTes.settest(a);
}
-> The above wont work as it returns Unknown Source.
The next way I tried is by using the MyModel , and using getter and setter methods, But unsuccessful.
I'm sorry my question is really long. I have tried but not succeeded.
What can I do to solve this?
Thanks in advance.
// my model looks like
public class MyModel {
private String btnname;
public String getBtnname() {
return btnname;
}
public void setBtnname(String btnname) {
this.btnname = btnname;
}
}
When you check the check box then in the controller of the FirstView (where you implement an event handler for the check box click) change the label text in your model.
Your model should be bound to your views therefore the label text in your SecondView should be updated.
If you did not bind the model to your views you may use an Observer pattern.
1.Change your model and extend java.util.Observable
public class MyModel extends Observable {
private String btnname;
public String getBtnname() {
return btnname;
}
public void setBtnname(String btnname) {
this.btnname = btnname;
pingObservers()
}
private void pingObservers() {
setChanged();
notifyObservers();
}
}
Register your SecondController as an Observer of the model. When you set the model to the controller add a line similar to this:
model.addObserver(this);
SecondController must implement java.util.Observer.update(...)
void update(Observable o, Object o1) {
// Set the label text with model value
}
In you event handler in the FirstController when you call the setBtnname() method on your model the update() method in the SecondController will be called. There up to you to add the code to change your label text. Since the label is in the view controlled by SecondController you just need to inject a reference of the label in the controller with #FXML annotation.

How to deal with Property<T>, a change listener and initialization of the property?

I am using JavaFX's Property<T> class and I am quite happy with the result, minified example code:
public CircularListCursor<E> {
private final Property<E> elementProperty;
public CircularListCursor() {
this.elementProperty = new SimpleObjectProperty(/*some value*/);
}
//various methods that call elementProperty.setValue(/*some value*/);
}
Usage:
private final CircularListCursor<SelectionData> selectionDataCursor;
...
selectionDataCursor.elementProperty().addListener((observableValue, oldValue, newValue) -> {
oldValue.getLabel().setStyle("-fx-text-fill: black");
newValue.getLabel().setStyle("-fx-text-fill: red");
});
Now this works almost perfectly, but it doesn't trigger on the construction of the object. It is logical that it works that way, because the property is not bound to yet during construction, so no change event can be fired either.
But I do want to be notified of the initial value during construction to allow for clean code, is there a way to do so?
There is no direct solution for that in JavaFX.
Nevertheless, you can make things a little bit easier/cleaner by moving the listener code into a private event handler method. This method can then be called once at the end of construction to initialize your object state. Thanks to Java 8 lambda expressions, you can use the method reference to the event handler method directly as listener:
// register event handler method
selectionDataCursor.elementProperty().addListener(this::onElementChanged);
// call listener once for initialization:
onElementChanged(selectionDataCursor.elementProperty(), null, selectionDataCursor.getElement());
...
// event handler method
private void onElementChanged(ObservableValue<? extends E> observableValue, E oldValue, E newValue) {
if (oldValue != null) oldValue.getLabel().setStyle("-fx-text-fill: black");
if (newValue != null) newValue.getLabel().setStyle("-fx-text-fill: red");
}
Side note: Listeners built via method references can't be removed any more. More specific, the following code will NOT remove the listener, as this::onElementChanged will create a new listener every time that is not equal to the one that is already registered:
selectionDataCursor.elementProperty().removeListener(this::onElementChanged);
Using EasyBind, you can
Select the nested styleProperty from elementProperty.
Bind the nested styleProperty to some observable string (in your case, a constant string for red text fill).
Provide an additional string argument to the bind method that is used to reset the style property of the old element when the element changes.
Here is the code:
ObservableValue<String> constRed = new SimpleStringProperty("-fx-text-fill: red");
EasyBind.monadic(selectionDataCursor.elementProperty())
.selectProperty(e -> e.getLabel().styleProperty())
.bind(constRed, "-fx-text-fill: black");
Notice how you don't need to register any listeners—one binding does it all. A binding is more declarative, while a listener is more imperative (side-effectful).

JavaFX & FXML: assign ChangeListener to observable property

According to the JavaFX tutorial it should be possible to register event handlers to observable properties in FXML:
Any class that defines a setOnEvent() method can be assigned an event handler in markup, as can any observable property (via an "onPropertyChange" attribute).
Now, I'm trying to register an event handler for the selected property of ToggleButton:
<ToggleButton text="%SomePane.fooButton.text" onSelectedChanged="#handleFooSelectedChanged" toggleGroup="$toggleGroup"/>
and in the controller:
#FXML
public void handleFooSelectedChanged(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
}
But I'm getting the following error:
Caused by: javafx.fxml.LoadException: Controller method
"handleFooSelectedChanged" not found.
Do I have to change the method signature? Is this a bug? Or is this not supported at all?
Your FXML-attribute is wrong! The pattern is on<PropertyName>Change (without 'd'), not on<PropertyName>Changed!
So this should work: onSelectedChange="#handleFooSelectedChanged"
Note: Your controller method can also look like this:
#FXML
public void handleFooSelectedChanged(BooleanProperty observable, boolean oldValue, boolean newValue);

Hijacking listeners in Java, passing in additional parameters

I am building a simple app and I am implementing it in a simple MVC pattern where the controller adds event handlers to the view. Here's a sample controller code attaching a handler to the UI.
Basically, the code adds an event handler when the UI's save button is clicked. The UI contains the name and id number entry. What I wanted to happen is to pass the name and id number into the actionPerformed function.
ui.onAddStudent(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
System.out.print("test");
}
});
And the receiving function in the UI (in another file) is the following.
public void onAddStudent(ActionListener handler){
//something missing here
addStudent.addActionListener(handler);
}
I am not really into Java because it's not my forte. I actually do JavaScript. Now, a similar handler In JavaScript, one can use the call() or apply() method to call the handler and pass in additional parameters. If the above code was in JS, it would be like
//in the controller
ui.onAddStudent(function(event,id,name){
//I can use id and name
});
//in the UI
ui.onAddStudent = function(handler){
//store into a cache
//add handler to the button
}
//when student is added (button clicked)
handler.call(this,event,id,name);
How do I do the same thing in Java?
You have two choices:
let it as it is, and have the controller get the ID and name from the GUI (and that is the easiest and simplest solution, IMHO)
use your own Event and Listener types, containing this information. For example:
public class StudentAddedEvent {
private long ID;
private String name;
...
}
public interface StudentAddedListener {
void studentAdded(StudentAddedEvent event);
}
The UI would register an ActionListener on the button, and this action listener would do:
#Override
public void actionPerformed(ActionEvent e) {
long id = getIdInGui();
String name = getNameInGui();
StudentAddedEvent event = new StudentAddedEvent(id, name);
for (StudentAddedListener listener : studentAddedListeners) {
listener.studentAdded(event);
}
}
You can define your own Actions too, and set those to the buttons (constructor argument or setAction) and other components.
Extend AbstractAction for that.

Categories