I faced the problem of not pulling nodes after I inherited from Class Pane, I don't know why this problem happened. Please find a solution to my problem
package assist.design.roots;
import javafx.beans.DefaultProperty;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
#DefaultProperty("children")
public class AdvancedPane extends Pane {
public AdvancedPane() {
super();
}
public AdvancedPane(Node... children) {
super(children);
getChildren().addAll(children);
}
public AdvancedPane(Node... children) {
super(children);
getChildren().addAll(children);
}
#Override
public ObservableList<Node> getChildren() {
return super.getChildren();
}
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
Update 3: solution
I finally got it: just save a reference of MyRoot in a property:
MainApp
package my.group.javafxtestbinding;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class MainApp extends Application {
private MyRoot root;
#Override
public void start(Stage primaryStage) {
root = new MyRoot();
HBox parent = root.myRoot;
Scene scene = new Scene(parent, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
Button gcButton = new Button("Collect Garbage");
gcButton.setOnMousePressed(event -> System.gc());
parent.getChildren().add(gcButton);
}
public static void main(String[] args) {
launch(args);
}
}
Update 3 solution end
Update 2:
So, I'm still having problems with losing my bindings and it's due to garbage collection; that I can reproduce. See the follwing Minimal Working Example.
The structure of the example is somewhat special, that is because I use the MVP framework from Adam Bien: afterburner.fx. And I wanted to simulate the same structure in my example.
I read a lot (i.e. here, here) and understand the basic problem but still can't really grasp it.
I'm somewhat new to JavaFX, hope somebody can give me a hint.
Reproduce:
When you click on an item in the table, the content of the label changes due to the binding between the selected model in the table (Select) and the label (Show). MyRoot manages both.
When you click on the button garbage collection is executed and the binding is gone.
MainApp
package my.group.javafxtestbinding;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class MainApp extends Application {
#Override
public void start(Stage primaryStage) {
MyRoot root = new MyRoot();
HBox parent = root.myRoot;
Scene scene = new Scene(parent, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
Button gcButton = new Button("Collect Garbage");
gcButton.setOnMousePressed(event -> System.gc());
parent.getChildren().add(gcButton);
}
public static void main(String[] args) {
launch(args);
}
}
MyRoot
package my.group.javafxtestbinding;
import javafx.scene.layout.HBox;
public class MyRoot {
public HBox myRoot;
private final Select select;
private final Show show;
public MyRoot() {
select = new Select();
show = new Show();
myRoot = new HBox();
myRoot.getChildren().addAll(select.myTable, show.myLabel);
show.selectedModelProperty().bind(select.selectedModelProperty());
}
}
Select
package my.group.javafxtestbinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
public class Select {
private ObjectProperty<MyModel> selectedModel;
public TableView<MyModel> myTable;
private final TableColumn<MyModel, String> myColumn;
public Select() {
this.selectedModel = new SimpleObjectProperty<>();
myTable = new TableView();
myColumn = new TableColumn("Name");
myTable.getColumns().add(myColumn);
myTable.setItems(getList());
myColumn.setCellValueFactory(cellData -> cellData.getValue().getNameProperty());
ChangeListener<MyModel> changeListener = new ChangeListener<MyModel>() {
#Override
public void changed(ObservableValue<? extends MyModel> observable, MyModel oldValue, MyModel newValue) {
selectedModel.set(newValue);
}
};
myTable.getSelectionModel().selectedItemProperty().addListener(changeListener);
}
public ObjectProperty<MyModel> selectedModelProperty() {
return selectedModel;
}
private ObservableList<MyModel> getList() {
MyModel model1 = new MyModel("Joe");
MyModel model2 = new MyModel("Jim");
MyModel model3 = new MyModel("Jack");
ObservableList<MyModel> list = FXCollections.observableArrayList(model1, model2, model3);
return list;
}
}
Show
package my.group.javafxtestbinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Label;
public class Show {
private ObjectProperty<MyModel> selectedModel;
public Label myLabel;
public Show() {
this.selectedModel = new SimpleObjectProperty<>();
myLabel = new Label("Init");
ChangeListener listener = new ChangeListener<MyModel>() {
#Override
public void changed(ObservableValue<? extends MyModel> observable, MyModel oldValue, MyModel newValue) {
if (newValue != null) {
myLabel.setText(newValue.getName());
}
}
};
this.selectedModel.addListener(listener);
}
public ObjectProperty<MyModel> selectedModelProperty() {
return selectedModel;
}
}
MyModel
package my.group.javafxtestbinding;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class MyModel {
private final StringProperty name;
public MyModel(String name) {
this.name = new SimpleStringProperty(name);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty getNameProperty() {
return name;
}
}
Update 2 end
Update 1:
Ok, thank you for your comments.
I thought I was doing it wrong. But if you say that this is the correct way then the problem must be somewhere else. I will post it when I find the problem...
Update 1 end
Original Post:
I have problems binding many properties to one observable.
Let's say I have three (or more) properties ObjectProperty<Foo> firstProp in FirstObject, ObjectProperty<Foo> secondProp in SecondObject and ObjectProperty<Foo> thirdProp in ThirdObject.
How can I bind second and third property to the first which is the observable.
So when firstProp changes the ChangeListeners attached to secondProp and thirdProp will be fired.
What I tried:
SecondObject.secondProperty().bind(FirstObject.firstProperty());
ThirdObject.thirdProperty().bind(FirstObject.firstProperty());
The result is that it sometimes works and sometimes not. If I add more bindings in the same way they do not work at all.
Is this not working because of this:
Note that JavaFX has all the bind calls implemented through weak listeners. This means the bound property can be garbage collected and stopped from being updated.
Javadoc bind()
I find a lot of answers to how to bind many observables to one property, but not the other way.
Can somebody point me in the right direction?
I know questions similar to this have been asked, and on different dates, but I'll put an SSCCE in here and try to ask this simply.
I would like to be able to update the data model, and have any views upon it automatically update, such that any caller updating the model is not aware of whatever views there presently are. This is what I learned/tried so far, and without calling TableView.refresh() it does not update. What am I missing?
main.java:
package application;
import javafx.application.Application;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
public class Main extends Application {
#Override
public void start(Stage stage) {
// data
ObservableList<Crew> data = FXCollections.observableArrayList();
data.addAll(new Crew(1, "A"), new Crew(2, "B"));
// table
TableColumn<Crew, Integer> crewIdCol = new TableColumn<Crew, Integer>("Crew ID");
crewIdCol.setCellValueFactory(new PropertyValueFactory<Crew, Integer>("crewId"));
crewIdCol.setMinWidth(120);
TableColumn<Crew, String> crewNameCol = new TableColumn<Crew, String>("Crew Name");
crewNameCol.setCellValueFactory(new PropertyValueFactory<Crew, String>("crewName"));
crewNameCol.setMinWidth(180);
TableView<Crew> table = new TableView<Crew>(data);
table.getColumns().addAll(crewIdCol, crewNameCol);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
// button
Button button = new Button(" test ");
button.setOnAction(ae -> {
// test
StringProperty nameProp = data.get(0).crewName();
if(nameProp.get().equals("A")) {
data.get(0).setCrewName("foo");
// table.refresh();
System.out.println("foo");
} else {
data.get(0).setCrewName("A");
// table.refresh();
System.out.println("A");
}
});
VBox box = new VBox(10);
box.setAlignment(Pos.CENTER);;
box.getChildren().addAll(table, button);
Scene scene = new Scene(box);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Crew.java
package application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Crew {
private final IntegerProperty crewId = new SimpleIntegerProperty();
private final StringProperty crewName = new SimpleStringProperty();
Crew(int id, String name) {
crewId.set(id);
crewName.set(name);
}
public IntegerProperty crewId() { return crewId; }
public final int getCrewId() { return crewId.get(); }
public final void setCrewId(int id) { crewId.set(id); }
public StringProperty crewName() { return crewName; }
public final String getCrewName() { return crewName.get(); }
public final void setCrewName(String name) { crewName.set(name); }
}
Your model class Crew has the "wrong" name for the property accessor methods. Without following the recommended method naming scheme, the (somewhat legacy code) PropertyValueFactory will not be able to find the properties, and thus will not be able to observe them for changes:
package application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Crew {
private final IntegerProperty crewId = new SimpleIntegerProperty();
private final StringProperty crewName = new SimpleStringProperty();
Crew(int id, String name) {
crewId.set(id);
crewName.set(name);
}
public IntegerProperty crewIdProperty() { return crewId; }
public final int getCrewId() { return crewId.get(); }
public final void setCrewId(int id) { crewId.set(id); }
public StringProperty crewNameProperty() { return crewName; }
public final String getCrewName() { return crewName.get(); }
public final void setCrewName(String name) { crewName.set(name); }
}
Alternatively, just implement the callback directly:
crewIdCol.setCellValueFactory(cellData -> cellData.getValue().crewIdProperty());
in which case the compiler will ensure that you use an existing method name for the property.
I have a custom ListCell with a ImageView as the graphic. I want this ImageView to catch MouseEvents (like mouse_over, or mouse_click). But it does not catch any events.
The custom ListCell however catches events.
Is this unusual behaviour or do I need to pass the Events from the ListCell to its graphic (somehow)?
If I add a Button, it gets Mouseevents, strangely.
If you have just a link of a working example, I would happily crawl my way through it ;)
Thanks for your effort.
Class CustomCell:
package test;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
public class CustomCell extends ListCell<String>{
ImageView removeTrack;
public CustomCell(){
removeTrack = new ImageView("https://lh3.googleusercontent.com/-lbN1Ca63JPs/AAAAAAAAAAI/AAAAAAAAAAQ/smvshnyosS4/s46-c-k/photo.jpg");
removeTrack.setOnMouseClicked(e -> {
System.out.println("test");
});
}
#Override
protected void updateItem(String t, boolean bln) {
super.updateItem(t, bln);
if (t != null) {
setText(t);
setGraphic(removeTrack);
setContentDisplay(ContentDisplay.RIGHT);
addEventFilter(MouseEvent.MOUSE_MOVED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
removeTrack.setVisible(true);
}
});
addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
removeTrack.setVisible(false);
}
});
}
}
}
Main-Class:
package test;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import javafx.util.Callback;
public class JAVAtest extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
ObservableList<String> names = FXCollections.observableArrayList(
"Julia", "Ian", "Sue", "Matthew", "Hannah", "Stephan", "Denise");
ListView lv = new ListView(names);
lv.setCellFactory(new Callback<ListView<String>, ListCell<String>>(){
#Override
public ListCell<String> call(ListView<String> p) {
return new CustomCell();
}
});
Scene scene = new Scene(lv);
stage.setScene(scene);
stage.show();
}
}
Well.. I can't see why your code isn't working. Seems like a bug.
It works fine if you wrap the image view in a container of some kind. For example:
import javafx.event.EventHandler;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
public class CustomCell extends ListCell<String> {
ImageView removeTrack;
StackPane imageContainer ;
public CustomCell() {
removeTrack = new ImageView(
"https://lh3.googleusercontent.com/-lbN1Ca63JPs/AAAAAAAAAAI/AAAAAAAAAAQ/smvshnyosS4/s46-c-k/photo.jpg");
removeTrack.setOnMouseClicked(e -> {
System.out.println("test");
});
imageContainer = new StackPane(removeTrack);
}
#Override
protected void updateItem(String t, boolean bln) {
super.updateItem(t, bln);
if (t != null) {
setText(t);
setGraphic(imageContainer);
setContentDisplay(ContentDisplay.RIGHT);
addEventFilter(MouseEvent.MOUSE_MOVED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
imageContainer.setVisible(true);
}
});
addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
imageContainer.setVisible(false);
}
});
} else {
setText(null);
setGraphic(null);
}
}
}
(As an aside: always handle all possible cases in the updateItem(...) method. Your code will have bugs if you start removing items or possibly while scrolling, because you don't handle the empty cell / null item case.)
I am learning JavaFx and building a sample app that would display a list of restaurant menu items in a scroll-able way. I figured out the best way to do this is using GridView control from controlsfx because even with the large set of menu items the scroll would work fast. Here is a sample code that I am trying to make it work:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
VBox root = new VBox();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
ObservableList<GridPane> list = FXCollections.<GridPane>observableArrayList();
GridView<GridPane> gridView = new GridView<>(list);
gridView.setCellFactory(new Callback<GridView<GridPane>, GridCell<GridPane>>() {
public GridCell<GridPane> call(GridView<GridPane> gridView) {
return new GridCell<GridPane>();
}
});
Label lblName1 = new Label("Name");
GridPane grid = new GridPane();
grid.addRow(0, lblName1);
Label lblName2 = new Label("Price");
grid.addRow(1, lblName2);
list.add(grid);
root.getChildren().add(gridView);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
When I run the code above it only shows the VBox with no item. I have tried to replicate the sample code given on controlsfx website on using GridView (http://controlsfx.bitbucket.org/org/controlsfx/control/GridView.html).
Any help/tip would be much appreciated. Thanks.
Figured out the problem and here is the correct code. I needed to write the custom GridPane (for dsplaying menu items) and the cell factory for generating the custom object.
//Main Class
package application;
import java.util.Random;
import org.controlsfx.control.GridCell;
import org.controlsfx.control.GridView;
import org.controlsfx.control.cell.ColorGridCell;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.util.Callback;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
VBox root = new VBox();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
GridView<MenuItem> menuItems = new GridView<>();
for(int i = 0; i < 10000; i++) {
menuItems.getItems().addAll(new MenuItem(i));
}
menuItems.setCellFactory(new MenuItemCellFactory());
root.getChildren().add(menuItems);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
/**
* Custom Menu Item
*/
package application;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
/**
* #author Rahul
*
*/
public class MenuItem extends GridPane {
private Integer name = null;
public MenuItem(int i) {
// TODO Auto-generated constructor stub
this.name = i;
}
public Integer getName() {
return name;
}
public void setName(Integer name) {
this.name = name;
}
}
//Menu Item Cell Factory
package application;
import org.controlsfx.control.GridCell;
import org.controlsfx.control.GridView;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.GridPane;
import javafx.util.Callback;
public class MenuItemCellFactory implements Callback<GridView<MenuItem>, GridCell<MenuItem>> {
#Override
public GridCell<MenuItem> call(GridView<MenuItem> listview) {
return new MenuItemCell();
}
}
//Menu Item Cell
package application;
import org.controlsfx.control.GridCell;
import javafx.scene.control.ListCell;
import javafx.scene.layout.GridPane;
public class MenuItemCell extends GridCell<MenuItem> {
#Override
protected void updateItem(MenuItem item, boolean empty) {
// TODO Auto-generated method stub
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.getName().toString());
setStyle("-fx-background-color: #ffffff; -fx-background-radius: 15; -fx-border-radius: 15; -fx-border-width: 0; -fx-padding: 10; -fx-pref-width: 145; -fx-max-width: 145; -fx-max-width: 145; -fx-pref-height: 130; -fx-max-height: 130; -fx-effect: dropshadow(three-pass-box, #93948d, 10, 0, 0, 0);");
}
}
}
This is a sample code to use GridView containing list of GridPane using cellfactory. Please modify it as per your requirement. The basic would remain the same.
Suggestions and feedback are most welcome. Thanks.
I have two classes. The first one (the starting class):
package
{
import flash.display.Sprite;
import flash.events.KeyboardEvent;
import tetris.*;
public class TetrisGame extends Sprite
{
private var _gameWell:Well;
public function TetrisGame()
{
_gameWell = new Well();
addChild(_gameWell);
}
}
}
The second:
package tetris
{
import flash.display.Sprite;
import flash.events.KeyboardEvent;
public class Well extends Sprite
{
public function Well()
{
super();
addEventListener(KeyboardEvent.KEY_DOWN, onKeyboard);
}
private function onKeyboard(event:KeyboardEvent):void
{
//some code is here
}
}
}
But when I press any buttons on my keyboard, the child class Well doesn't have any reaction. What's the problem?
OK, I get it! =))
I should set focus on the child sprite so it can listen for keyboard events.
package
{
import flash.display.Sprite;
import flash.events.KeyboardEvent;
import tetris.*;
public class TetrisGame extends Sprite
{
private var _gameWell:Well;
public function TetrisGame()
{
_gameWell = new Well();
addChild(_gameWell);
stage.focus = _gameWell;
}
}
}
Or as an alternative; add the event listener to the stage, so it doesn't depend on the Well having focus.
package tetris
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
public class Well extends Sprite
{
public function Well():void
{
super();
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyboard);
}
private function onKeyboard(event:KeyboardEvent):void
{
//some code is here
}
}
}