I have created the treeview in javafx2. i need to write the event for getting clicked node name.
pls let me know how to do this?
use ChangeListener .
Sample code :
treeView.getSelectionModel().selectedItemProperty().addListener( new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue,
Object newValue) {
TreeItem<String> selectedItem = (TreeItem<String>) newValue;
System.out.println("Selected Text : " + selectedItem.getValue());
// do what ever you want
}
});
Slightly more updated version, using lambdas which cleans it up a bit:
treeView.getSelectionModel()
.selectedItemProperty()
.addListener((observable, oldValue, newValue) -> System.out.println("Selected Text : " + newValue.getValue()));
slightly updated version
treeView.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<TreeItem<String>>() {
#Override
public void changed(
ObservableValue<? extends TreeItem<String>> observable,
TreeItem<String> old_val, TreeItem<String> new_val) {
TreeItem<String> selectedItem = new_val;
System.out.println("Selected Text : " + selectedItem.getValue());
// do what ever you want
}
});
Related
I'm using a TextField to display the path of a directory the user has opened in my application.
Currently, if the path can't fit inside the TextField, upon focusing away/clicking away from this control, it looks like as if the path has become truncated:
I want the behaviour of TextField set such that when I focus away from it, the path shown inside automatically scrolls to the right and the user is able to see the directory they've opened. I.e. something like this:
How can I achieve this? I've tried adapting the answer given from here
as follows in initialize() method in my FXML Controller class:
// Controller class fields
#FXML TextField txtMoisParentDirectory;
private String moisParentDirectory;
// ...
txtMoisParentDirectory.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldStr, String newStr) {
moisParentDirectory = newStr;
txtMoisParentDirectory.selectPositionCaret(moisParentDirectory.length());
txtMoisParentDirectory.deselect();
}
});
However it doesn't work.
Your problem is based on two events, the length of the text entered and the loss of focus, so to solve it I used the properties textProperty() and focusedProperty() and here is the result :
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Launcher extends Application{
private Pane root = new Pane();
private Scene scene;
private TextField tf = new TextField();
private TextField tft = new TextField();
private int location = 0;
#Override
public void start(Stage stage) throws Exception {
scrollChange();
tft.setLayoutX(300);
root.getChildren().addAll(tft,tf);
scene = new Scene(root,400,400);
stage.setScene(scene);
stage.show();
}
private void scrollChange(){
tf.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
location = tf.getText().length();
}
});
tf.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if(!newValue){
Platform.runLater( new Runnable() {
#Override
public void run() {
tf.positionCaret(location);
}
});
}
}
});
}
public static void main(String[] args) {
launch(args);
}
}
And concerning the Platform.runLater I added it following this answer Here I don't know why it does not work without it, good luck !
tf.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
int location = tf.getText().length();
Platform.runLater(() -> {
tf.positionCaret(location);
});
}
});
this is also work
Since the other answers didn't work for me here is a solution that should do the trick:
private TextField txtField;
// Both ChangeListeners just call moveCaretToEnd(), we need them both because of differing data types we are listening to
private final ChangeListener<Number> caretChangeListener = (observable, oldValue, newValue) -> moveCaretToEnd();
private final ChangeListener<String> textChangeListener = (observable, oldValue, newValue) -> moveCaretToEnd();
// This method moves the caret to the end of the text
private void moveCaretToEnd() {
Platform.runLater(() -> {
txtField.deselect();
txtField.end();
});
}
public void initialize() {
// Immediatly add the listeners on initialization (or once you created the TextField if you are not using FXML)
txtField.caretPositionProperty().addListener(caretChangeListener);
txtField.textProperty().addListener(textChangeListener);
txtField.focusedProperty().addListener((observable, oldValue, isFocused) -> {
if (isFocused) {
// once the TextField has been focused remove the listeners to enable normal editing of the text
txtField.caretPositionProperty().removeListener(caretChangeListener);
txtField.textProperty().removeListener(textChangeListener);
} else {
// when the focus is lost apply the listeners again
moveCaretToEnd();
txtField.caretPositionProperty().addListener(caretChangeListener);
txtField.textProperty().addListener(textChangeListener);
}
});
}
i have TextField contains a text like: "hello!" and this text if it changed to another text should do something the main problem is how to check if this TextField contains new value (new text) instead of the first text wrote should do something. can any body show me how to do that and thanks in advance :)
You can add a ChangeListener to the textProperty of the TextField like this:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FlowPane root = new FlowPane();
TextField textField = new TextField();
textField.textProperty().addListener(new ChangeListener<String>(){
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
System.out.println(observable + ", " + oldValue + ", " + newValue);
}
});
root.getChildren().add( textField);
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
My application has to deal with dynamic bindings.
I have a list of Java Bean objects that has a lot of attributes that must be editted.
There are multiple types of objects with different attributes.
I create a TreeView to list the objects.
Each time I select one object in the TreeView, I update a second container in the screen where I create dynamically the Labels and TextFields that are bound to the current object's properties.
I use JavaBeanStringProperty, JavaBeanIntegerProperty, and other objects of this family to create a property to interact with the Java Bean. That works perfectly.
I link each of those JavaBeanProperty objects to their corresponding TextField's TextAttribute so I can update the Bean when the UI is changed and vice-versa.
The problem is: Everytime I select a new Java Bean in the TreeView, all the objects previously created dynamically seem to be still alive. It will work for the first time I select the Bean and edit that, but for the second time further, it won't work.
I tried creating a list of created bindings so I can unbind them when selecting a new Bean, however it is not possible, since StringProperties and IntegerProperties do not share a common interface so I can unbind them.
Does anyone have an idea on how to deal with that?
Example:
Beans and their properties:
Bean1: name (String), amount (integer)
Bean2: name (String), type (String)
Bean3: name (String), address (string)
If I select the Bean1, I clear the container, and add those new objects to that:
A TextField to represent the name, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectinally with the text field's TextProperty.
A TextField to represent the amount, a JavaBeanIntegerProperty to interface with the Bean, and bind it bidirectionally with the text field's TextProperty using a NumberConverter.
When I select the Bean2, I clear the container, and add those new objects to that:
A TextField to represent the name, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectinally with the text field's TextProperty.
A TextField to represent the type, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectionally with the text field's TextProperty.
When I select the Bean3, I clear the container, and add those new objects to that:
A TextField to represent the name, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectinally with the text field's TextProperty.
A TextField to represent the address, a JavaBeanStringProperty to interface with the Bean, and bind it bidirectionally with the text field's TextProperty.
Here is a complete code example:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.ref.WeakReference;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.adapter.JavaBeanIntegerProperty;
import javafx.beans.property.adapter.JavaBeanIntegerPropertyBuilder;
import javafx.beans.property.adapter.JavaBeanStringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.converter.NumberStringConverter;
public class Main extends Application {
//=============================================================================================
public abstract class Bean {
public abstract void createUI(Pane container);
}
//=============================================================================================
public class Bean1 extends Bean {
private String name;
private int amount;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public Bean1(String name, int amount) {
this.name = name;
this.amount = amount;
}
public String toString() { return name; }
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
System.out.println("Bean 1 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly added.");
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
System.out.println("Bean 1 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly removed.");
}
public String getName() { return name; }
public void setName(String name) {
String last = this.name;
this.name = name;
pcs.firePropertyChange("name", last, this.name);
System.out.println("Bean 1: name changed: " + last + " -> " + this.name);
}
public int getAmount() { return amount; }
public void setAmount(int amount) {
int last = this.amount;
this.amount = amount;
pcs.firePropertyChange("amount", last, this.amount);
System.out.println("Bean 1: amount changed: " + last + " -> " + this.amount);
}
public void createUI(Pane container) {
HBox nameContainer = new HBox();
Label nameLabel = new Label("name: ");
nameLabel.setPrefWidth(80);
TextField nameValue = new TextField();
nameValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("name").build();
nameValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 1 name property.");
}
nameContainer.getChildren().addAll(nameLabel, nameValue);
HBox amountContainer = new HBox();
Label amountLabel = new Label("amount: ");
amountLabel.setPrefWidth(80);
TextField amountValue = new TextField();
amountValue.setPrefWidth(140);
try {
JavaBeanIntegerProperty wrapper = new JavaBeanIntegerPropertyBuilder().bean(this).name("amount").build();
Bindings.bindBidirectional(amountValue.textProperty(), wrapper, new NumberStringConverter());
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 1 amount property.");
}
amountContainer.getChildren().addAll(amountLabel, amountValue);
container.getChildren().clear();
container.getChildren().addAll(nameContainer, amountContainer);
}
}
//=============================================================================================
public class Bean2 extends Bean {
private String name;
private String type;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public Bean2(String name, String type) {
this.name = name;
this.type = type;
}
public String toString() { return name; }
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
System.out.println("Bean 2 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly added.");
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
System.out.println("Bean 2 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly removed.");
}
public String getName() { return name; }
public void setName(String name) {
String last = this.name;
this.name = name;
pcs.firePropertyChange("name", last, this.name);
System.out.println("Bean 2: name changed: " + last + " -> " + this.name);
}
public String getType() { return type; }
public void setType(String type) {
String last = this.type;
this.type = type;
pcs.firePropertyChange("type", last, this.type);
System.out.println("Bean 2: type changed: " + last + " -> " + this.type);
}
public void createUI(Pane container) {
HBox nameContainer = new HBox();
Label nameLabel = new Label("name: ");
nameLabel.setPrefWidth(80);
TextField nameValue = new TextField();
nameValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("name").build();
nameValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 2 name property.");
}
nameContainer.getChildren().addAll(nameLabel, nameValue);
HBox typeContainer = new HBox();
Label typeLabel = new Label("type: ");
typeLabel.setPrefWidth(80);
TextField typeValue = new TextField();
typeValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("type").build();
typeValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 2 type property.");
}
typeContainer.getChildren().addAll(typeLabel, typeValue);
container.getChildren().clear();
container.getChildren().addAll(nameContainer, typeContainer);
}
}
//=============================================================================================
public class Bean3 extends Bean {
private String name;
private String address;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public Bean3(String name, String address) {
this.name = name;
this.address = address;
}
public String toString() { return name; }
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
System.out.println("Bean 3 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly added.");
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
System.out.println("Bean 3 PCS has " + pcs.getPropertyChangeListeners().length + " listeners. 1 was possuibly removed.");
}
public String getName() { return name; }
public void setName(String name) {
String last = this.name;
this.name = name;
pcs.firePropertyChange("name", last, this.name);
System.out.println("Bean 3: name changed: " + last + " -> " + this.name);
}
public String getAddress() { return address; }
public void setAddress(String address) {
String last = this.address;
this.address = address;
pcs.firePropertyChange("type", last, this.address);
System.out.println("Bean 3: address changed: " + last + " -> " + this.address);
}
public void createUI(Pane container) {
HBox nameContainer = new HBox();
Label nameLabel = new Label("name: ");
nameLabel.setPrefWidth(80);
TextField nameValue = new TextField();
nameValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("name").build();
nameValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 3 name property.");
}
nameContainer.getChildren().addAll(nameLabel, nameValue);
HBox addressContainer = new HBox();
Label addressLabel = new Label("type: ");
addressLabel.setPrefWidth(80);
TextField addressValue = new TextField();
addressValue.setPrefWidth(140);
try {
JavaBeanStringProperty wrapper = new JavaBeanStringPropertyBuilder().bean(this).name("address").build();
addressValue.textProperty().bindBidirectional(wrapper);
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("Exception binding Bean 3 address property.");
}
addressContainer.getChildren().addAll(addressLabel, addressValue);
container.getChildren().clear();
container.getChildren().addAll(nameContainer, addressContainer);
}
}
//=============================================================================================
private class TreeItemRefresher implements PropertyChangeListener {
private String property;
private WeakReference<TreeItem<Bean>> treeItem;
TreeItemRefresher(String property, TreeItem<Bean> treeItem) {
this.property = property;
this.treeItem = new WeakReference<>(treeItem);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (property.equals(evt.getPropertyName())) {
// Workaround to repaint the tree item when its value object changes.
TreeItem<Bean> item = treeItem.get();
if (item != null) {
item.setExpanded(false);
item.setExpanded(true);
}
}
}
}
//=============================================================================================
private TreeView<Bean> treeView = new TreeView<>();
private VBox container = new VBox();
//=============================================================================================
#SuppressWarnings("unchecked")
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Dynamic Bindings tests.");
HBox mainContainer = new HBox();
container.setPadding(new Insets(10));
// Creating beans.
Bean1 bean1 = new Bean1("Bean 1", 10);
Bean2 bean2 = new Bean2("Bean 2", "Type O");
Bean3 bean3 = new Bean3("Bean 3", "10, Central Park Av.");
// Creating TreeView
treeView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<Bean>>() {
#Override
public void changed(ObservableValue<? extends TreeItem<Bean>> arg0, TreeItem<Bean> oldValue, TreeItem<Bean> newValue) {
Bean newItem = newValue.getValue();
newItem.createUI(container);
}
});
TreeItem<Bean> bean1item = new TreeItem<Bean>(bean1);
bean1.addPropertyChangeListener(new TreeItemRefresher("name", bean1item));
TreeItem<Bean> bean2item = new TreeItem<Bean>(bean2);
bean2.addPropertyChangeListener(new TreeItemRefresher("name", bean2item));
TreeItem<Bean> bean3item = new TreeItem<Bean>(bean3);
bean3.addPropertyChangeListener(new TreeItemRefresher("name", bean3item));
bean1item.setExpanded(true);
treeView.setRoot(bean1item);
bean1item.getChildren().addAll(bean2item, bean3item);
mainContainer.getChildren().addAll(treeView, container);
Scene scene = new Scene(mainContainer, 500, 300, Color.WHITE);
primaryStage.setScene(scene);
primaryStage.show();
}
//=============================================================================================
public Main() {
}
//=============================================================================================
public static void main(String[] args) {
launch(Main.class, args);
}
}
Notice sometimes changes to a field is not handled perfectly.
Notice also the number of PropertyChangeListeners are always increasing as you change the selected Bean. I know why that happens, but I really don't know how to deal with that.
Is there a better way to do it?
Notice I can't change the Bean objects. They are not under my control.
Thanks very much.
Did you try using JFXtras BeanPathAdaptor? It is a wonderful library for dynamic bindings based on POJOS.
You just have to use
beanPathAdaptor.setBean(myNewBean);
When you want to change your bean!
Here is a link:
http://ugate.wordpress.com/2012/07/30/javafx-programmatic-pojo-expression-bindings-part-iii/
EDIT
The other way I see is to define 3 custom components in JavaFX, one for each type. In them you define the complete binding to your bean with the view binded to your bean and add an input method such as "setBean1(Bean1 bean)"
Then, on selection you instanciate this component, show it and set its content with "setBean1(...)". You can do the same with the other types. Of course all of the bindings are static now, but the creation and use of components are dynamics, it is component-oriented and is high-coupled (view-model).
To optimize it, you can keep all the already created components in memory and use them again after to prevent from paying the instanciation of the view again, best way should be setting them to visible==false when you don't use them, you could even make an eager instanciation of all possible cases of components at initialization if you prefer.
I'm creating a TableView to show information regarding a list of custom objects (EntityEvents).
The table view must have 2 columns.
First column to show the corresponding EntityEvent's name.
The second column would display a button. The button text deppends on a property of the EntityEvent. If the property is ZERO, it would be "Create", otherwise "Edit".
I managed to do it all just fine, except that I can't find a way to update the TableView line when the corresponding EntityEvent object is changed.
Very Important: I can't change the EntityEvent class to use JavaFX properties, since they are not under my control. This class uses PropertyChangeSupport to notify listeners when the monitored property is changed.
Note:
I realize that adding new elements to the List would PROBABLY cause the TableView to repaint itself, but that is not what I need. I say PROBABLY because I've read about some bugs that affect this behavior.
I tried using this approach to force the repaint, by I couldn't make it work.
Does anyone knows how to do it?
Thanks very much.
Here is a reduced code example that illustrates the scenario:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Main extends Application {
//=============================================================================================
public class EntityEvent {
private String m_Name;
private PropertyChangeSupport m_NamePCS = new PropertyChangeSupport(this);
private int m_ActionCounter;
private PropertyChangeSupport m_ActionCounterPCS = new PropertyChangeSupport(this);
public EntityEvent(String name, int actionCounter) {
m_Name = name;
m_ActionCounter = actionCounter;
}
public String getName() {
return m_Name;
}
public void setName(String name) {
String lastName = m_Name;
m_Name = name;
System.out.println("Name changed: " + lastName + " -> " + m_Name);
m_NamePCS.firePropertyChange("Name", lastName, m_Name);
}
public void addNameChangeListener(PropertyChangeListener listener) {
m_NamePCS.addPropertyChangeListener(listener);
}
public int getActionCounter() {
return m_ActionCounter;
}
public void setActionCounter(int actionCounter) {
int lastActionCounter = m_ActionCounter;
m_ActionCounter = actionCounter;
System.out.println(m_Name + ": ActionCounter changed: " + lastActionCounter + " -> " + m_ActionCounter);
m_ActionCounterPCS.firePropertyChange("ActionCounter", lastActionCounter, m_ActionCounter);
}
public void addActionCounterChangeListener(PropertyChangeListener listener) {
m_ActionCounterPCS.addPropertyChangeListener(listener);
}
}
//=============================================================================================
private class AddPersonCell extends TableCell<EntityEvent, String> {
Button m_Button = new Button("Undefined");
StackPane m_Padded = new StackPane();
AddPersonCell(final TableView<EntityEvent> table) {
m_Padded.setPadding(new Insets(3));
m_Padded.getChildren().add(m_Button);
m_Button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// Do something
}
});
}
#Override protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(m_Padded);
m_Button.setText(item);
}
}
}
//=============================================================================================
private ObservableList<EntityEvent> m_EventList;
//=============================================================================================
#SuppressWarnings("unchecked")
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Table View test.");
VBox container = new VBox();
m_EventList = FXCollections.observableArrayList(
new EntityEvent("Event 1", -1),
new EntityEvent("Event 2", 0),
new EntityEvent("Event 3", 1)
);
final TableView<EntityEvent> table = new TableView<EntityEvent>();
table.setItems(m_EventList);
TableColumn<EntityEvent, String> eventsColumn = new TableColumn<>("Events");
TableColumn<EntityEvent, String> actionCol = new TableColumn<>("Actions");
actionCol.setSortable(false);
eventsColumn.setCellValueFactory(new Callback<CellDataFeatures<EntityEvent, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<EntityEvent, String> p) {
EntityEvent event = p.getValue();
event.addActionCounterChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent event) {
// TODO: I'd like to update the table cell information.
}
});
return new ReadOnlyStringWrapper(event.getName());
}
});
actionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<EntityEvent, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<EntityEvent, String> ev) {
String text = "NONE";
if(ev.getValue() != null) {
text = (ev.getValue().getActionCounter() != 0) ? "Edit" : "Create";
}
return new ReadOnlyStringWrapper(text);
}
});
// create a cell value factory with an add button for each row in the table.
actionCol.setCellFactory(new Callback<TableColumn<EntityEvent, String>, TableCell<EntityEvent, String>>() {
#Override
public TableCell<EntityEvent, String> call(TableColumn<EntityEvent, String> personBooleanTableColumn) {
return new AddPersonCell(table);
}
});
table.getColumns().setAll(eventsColumn, actionCol);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
// Add Resources Button
Button btnInc = new Button("+");
btnInc.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent ev) {
System.out.println("+ clicked.");
EntityEvent entityEvent = table.getSelectionModel().getSelectedItem();
if (entityEvent == null) {
System.out.println("No Event selected.");
return;
}
entityEvent.setActionCounter(entityEvent.getActionCounter() + 1);
// TODO: I expected the TableView to be updated since I modified the object.
}
});
// Add Resources Button
Button btnDec = new Button("-");
btnDec.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent ev) {
System.out.println("- clicked.");
EntityEvent entityEvent = table.getSelectionModel().getSelectedItem();
if (entityEvent == null) {
System.out.println("No Event selected.");
return;
}
entityEvent.setActionCounter(entityEvent.getActionCounter() - 1);
// TODO: I expected the TableView to be updated since I modified the object.
}
});
container.getChildren().add(table);
container.getChildren().add(btnInc);
container.getChildren().add(btnDec);
Scene scene = new Scene(container, 300, 600, Color.WHITE);
primaryStage.setScene(scene);
primaryStage.show();
}
//=============================================================================================
public Main() {
}
//=============================================================================================
public static void main(String[] args) {
launch(Main.class, args);
}
}
Try the javafx.beans.property.adapter classes, particularly JavaBeanStringProperty and JavaBeanIntegerProperty. I haven't used these, but I think you can do something like
TableColumn<EntityEvent, Integer> actionCol = new TableColumn<>("Actions");
actionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<EntityEvent, Integer> ev) {
return new JavaBeanIntegerPropertyBuilder().bean(ev.getValue()).name("actionCounter").build();
});
// ...
public class AddPersonCell extends TableCell<EntityEvent, Integer>() {
final Button button = new Button();
public AddPersonCell() {
setPadding(new Insets(3));
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
button.setOnAction(...);
}
#Override
public void updateItem(Integer actionCounter, boolean empty) {
if (empty) {
setGraphic(null);
} else {
if (actionCounter.intValue()==0) {
button.setText("Create");
} else {
button.setText("Add");
}
setGraphic(button);
}
}
}
As I said, I haven't used the Java bean property adapter classes, but the idea is that they "translate" property change events to JavaFX change events. I just typed this in here without testing, but it should at least give you something to start with.
UPDATE: After a little experimenting, I don't think this approach will work if your EntityEvent is really set up the way you showed it in your code example. The standard Java beans bound properties pattern (which the JavaFX property adapters rely on) has a single property change listener and an addPropertyChangeListener(...) method. (The listeners can query the event to see which property changed.)
I think if you do
public class EntityEvent {
private String m_Name;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private int m_ActionCounter;
public EntityEvent(String name, int actionCounter) {
m_Name = name;
m_ActionCounter = actionCounter;
}
public String getName() {
return m_Name;
}
public void setName(String name) {
String lastName = m_Name;
m_Name = name;
System.out.println("Name changed: " + lastName + " -> " + m_Name);
pcs.firePropertyChange("name", lastName, m_Name);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
public int getActionCounter() {
return m_ActionCounter;
}
public void setActionCounter(int actionCounter) {
int lastActionCounter = m_ActionCounter;
m_ActionCounter = actionCounter;
System.out.println(m_Name + ": ActionCounter changed: " + lastActionCounter + " -> " + m_ActionCounter);
pcs.firePropertyChange("ActionCounter", lastActionCounter, m_ActionCounter);
}
}
it will work with the adapter classes above. Obviously, if you have existing code calling the addActionChangeListener and addNameChangeListener methods you would want to keep those existing methods and the existing property change listeners, but I see no reason you can't have both.
I would like to have an action performed when I select an item from my listview in javafx 2.
I use a Netbeans JavaFX fxml application and SceneBuilder.
The OnMouseClicked method in SceneBuilder did not work. It gave me back an error that it can't find the method that I have already declared.
Can someone tell me how they managed to get it to work?
You cannot do it in FXML file alone.
Define the corresponding listView (assuming fx:id="myListView" in FXML) in Controller class of the FXML file:
#FXML
private ListView<MyDataModel> myListView;
Add listener in init/start method which will listen to the list view item changes:
myListView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<MyDataModel>() {
#Override
public void changed(ObservableValue<? extends MyDataModel> observable, MyDataModel oldValue, MyDataModel newValue) {
// Your action here
System.out.println("Selected item: " + newValue);
}
});
MyDataModel can be your own data structure model class or simply a String.
For String example,
#FXML
private ListView<String> myListView;
...
...
ObservableList<String> data = FXCollections.observableArrayList("chocolate", "blue");
myListView.setItems(data);
myListView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
// Your action here
System.out.println("Selected item: " + newValue);
}
});