Pass object from one scene to another - javafx-2

As I'm learning the new world of JavaFX2 I stumbled on another annoying problem. I'm developing a program with multiple scenes (~10 scenes). For that I created a small class like this:
public class SceneSelector {
...
public void setScene(Stage stage, String fxmlfilename, ObservableList ol) throws Exception{
String s = "../" + fxmlfilename;
Parent root = FXMLLoader.load(getClass().getResource(s));
root.setUserData(ol);
Scene scene = new Scene(root);
stage.setScene(scene);
//show the stage
stage.show();
}
}
This class works good enough for switching between the scenes.
Now the problem is that I sometimes need to pass data from Scene1 to Scene2. I'm trying to do this by setting the setUserData() for the new scene which basicly works exept for one thing. How can I get the userdata when the new Scene is beeing initialized? (because the Nodes are still null at that time)
Code at scene1:
//Code connected to a button that opens the new Scene
private void openLabID(ActionEvent event) throws Exception {
final Stage primaryStage = (Stage) btnNewScene.getScene().getWindow();
ObservableList<Koe> olAfTeWerkenKoeien = DA_Koe.getAfTeWerkenKoeien();
ss.setScene(primaryStage, "GUI/scenes/koe/Koe.fxml", olAfTeWerkenKoeien);
}
Code at scene2:
public void initialize(URL url, ResourceBundle rb) {
Scene s = lbl.getScene();
ObservableList<Koe> olAfTeWerkenKoeien = (ObservableList<Koe>) s.getRoot().getUserData();
System.out.println(olAfTeWerkenKoeien.size());
}
Of course Scene s gives a null value at this point (because lbl is null at this point), so I wonder, is there a method that is beeing fired right after initialize?
When I attach this code to a button on Scene2, it works like a charm, but it should be loaded automatically.
EDIT:
The setting of the data with the setMyData() method is not a problem, however retrieving it is:
public ObservableList<Koe> getMyData() {
return this.myData;
}
How can I get the CustomScene object when a controller initializes? Because doing this below will result in a NullPointerException (because btnSluiten is not initialized just yet):
#Override
public void initialize(URL url, ResourceBundle rb) {
...
Stage stage = (Stage) btnSluiten.getScene().getWindow();
CustomScene cs = (CustomScene) stage.getScene();
ObservableList<Koe> olKoe = cs.getMyData();
System.out.println(olKoe.size());
}

I believe you missed the point within the Scene object. From the Scene class documentation we can see that:
The JavaFX Scene class is the container for all content in a scene graph.
Which means that the Scene object is just a container and as such it's not supposed to hold any data.
With that in mind, you can make another static object with a field such as
private static Label lbl;
...
public static Label getLbl()
{
return MyStaticObject.Lbl;
}
...
and use it to store your lbl (or whatever object suits your information) and then statically retrieve it.
I'm doing that to set the owner of my other Stage objects from my application. I hope it helps. Cheers

If you really want your scene to be meaningful (aka store specific user data) you can extend it:
public class FooScene extends Scene {
private ObservableList myData;
public setMyData(ObservableList data) {
this.myData = data;
//handle data
}
}
The easiest way to make sure setup code is called after scene initialized it to call it by yourself:
public class SceneSelector {
...
public void setScene(Stage stage, String fxmlfilename, ObservableList ol) throws Exception{
String s = "../" + fxmlfilename;
Parent root = FXMLLoader.load(getClass().getResource(s));
// first: add root to scene
FooScene scene = new FooScene(root);
// second: apply data to scene (or root)
scene.setMyData(ol);
stage.setScene(scene);
//show the stage
stage.show();
}
}

You can use controller for scenes and pass the data through controller:
String filePath1 = "../" + fxmlfilename;
URL location1 = YourController1.class.getResource(filePath1);
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(location1);
fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
Parent root = (Node) fxmlLoader.load(location1.openStream());
YourController1 ctrl1 = (YourController1) fxmlLoader.getController();
Then you can assign data to the controller:
ctrl1.setUserData();
Finally, just show the scene as you want:
Scene scene = new Scene(root);
stage.setScene(scene);
//show the stage
stage.show();
In the initialize() method in the controller, just get data from controller as usual data object.

Some Addition of #Sergey Grinev :
Create A custom Scene :
package sample;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class DataPassingScene extends Scene {
public DataPassingScene(Parent parent) {
super(parent);
}
String tafsir;
public String getTafsir() {
return tafsir;
}
public void setTafsir(String tafsir) {
this.tafsir = tafsir;
}
}
Suppose your Main Class Name is App.java, Then Create a method to show new Stage :
public static void showLayout (Stage primaryStage, String fxmlFileName, String stringData) throws IOException {
Parent root = FXMLLoader.load(Objects.requireNonNull(App.class.getClassLoader().getResource(fxmlFileName)));
DataPassingScene scene = new DataPassingScene(root);
scene.setTafsir(stringData); // Here we pass the data
primaryStage.setScene(scene);
primaryStage.show();
}
Now When you want to pass the Data, Call the above method from any Where / any class in your app with some Data :
String tafsir = "This My Data" ;
try {
App.showLayout(new Stage(), "showTafsirFxml.fxml",tafsir);
} catch (IOException e) {
e.printStackTrace();
}
Then Get the data in your controller. To get the scene you have to get the stage, and to get the stage you have use one of your elements of FXML, suppose here your element is a button, called closeButton, So :
#FXML
private Button closeButton;
#Override
public void initialize(URL url, ResourceBundle rb) {
Platform.runLater(new Runnable() {
#Override public void run() {
Stage stage = (Stage) closeButton.getScene().getWindow();
DataPassingScene scene = (DataPassingScene) stage.getScene();
String s = scene.getTafsir(); // Here we get the Data
if(s!=null)
System.out.println("This is Tafsir From Highlight Table: "+s);
else
System.out.println("Data Passing Null");
}});
}
because, You have to wait some Times in above runLater, because for initializing scene take some time. Other wise scene will be null.

Related

Use of setPreferredSize is disregarded by program

How do I successfully call setPreferredSize in a method? I'm calling setPreferredSize twice. If I remove the call inside the constructor, the panel doesn't appear at all, whereas it had earlier appeared with the undesired size (500,300). This demonstrates that setPreferredSize is being executed in the constructor, but not in the method of the same class. Note that this is the only issue (as far as I have tested) with my code; there's no unexpected interference outside the code below.
...
public abstract class XYGrapher extends JPanel{
...
JFrame frame;
JPanel contentPane;
...
public XYGrapher() {
frame = new JFrame("Grapher");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = new JPanel();
contentPane.setPreferredSize(new Dimension(500, 300));
contentPane.setLayout(new SpringLayout());
this.setPreferredSize(new Dimension(500, 300));
frame.setContentPane(contentPane);
frame.pack();
frame.setVisible(true);
contentPane.add(this);
}
//
public void drawGraph(int xPixelStart, int yPixelStart, int pixelsWide, int pixelsHigh) {
...
contentPane.setPreferredSize(new Dimension(pixelsWide, pixelsHigh));
this.setPreferredSize(new Dimension(pixelsWide, pixelsHigh));
...
}
//*/
}
For reference, this is how XYGrapher eventually gets used:
public class GrapherTester extends XYGrapher{
...
public static void main(String[] args) {
GrapherTester g = new GrapherTester();
g.drawGraph(0,0,100,100);
}
}
I have managed to fix the issue in the meantime. Simply add
frame.pack();
to the method.

FXML parent element not injected in child controller [duplicate]

I am having the following problem with a program that I am currently writing, and I have searched on the internet, but I couldn't really find anything to help me understand the following problem
So inside another class I have written a method that executes this whenever the search button is clicked and the method looks like this:
public void searchButton(){
try {
new SearchController().display();
} catch (IOException e) {
e.printStackTrace();
}
}
And then the SearchController class looks something like this (I simplified it here):
public class SearchController {
#FXML
private Button cancelButton;
#FXML
private Label what;
private static Stage stage;
private static BorderPane borderPane;
#FXML
public void initialize(){
what.setText("Testing"); // this woks
cancelButton.setOnAction(e -> stage.close());
}
public void display() throws IOException {
stage = new Stage();
stage.setResizable(false);
stage.setTitle("Product search");
stage.initModality(Modality.APPLICATION_MODAL);
FXMLLoader loader = new FXMLLoader();
loader.setLocation(SearchController.class.getResource("Search.fxml"));
borderPane = loader.load();
Scene scene = new Scene(borderPane);
stage.setScene(scene);
//what.setText("Testing") and this doesn't work
stage.showAndWait();
}
}
Can someone please tell me why it is possible to write text on the initialize method (that method gets called after the borderPane = loader.load(); line...so why doesn't it work if I try to write on the label after that line?)
Thank you in advance
The FXMLLoader creates an instance of the class specified in the fx:controller attribute of the FXML root element. It then injects the elements defined in the FXML file into the controller instance it created when the fx:id attributes match the field names. Then it calls the initialize() method on that instance.
You create an instance of the controller "by hand" with new SearchController(). This is not the same object that is created by the FXMLLoader. So now when you have loaded the fxml file you have two different instances of SearchController. So if you call what.setText(...) from the display() method, you are not calling it on the controller instance created by the FXMLLoader. Consequently, what has not been initialized in the instance on which you are calling what.setText(...), and you get a null pointer exception.
Since initialize() is invoked by the FXMLLoader on the instance it created, when you call what.setText(...) from the initialize() method, you are calling it on the instance created by the FXMLLoader, and so the FXML-injected fields for that instance have been initialized.

duplicate view upon data change?

My CardView duplicate elements upon data change, the vardView is within a tab, and the way i declared that tab fragment as following;
in the onCreateView, i declared all the necessary firebase links and value events listeners to retrieve the required data related to the elements displayed on the cards.
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if(snapshot !=null){
for (DataSnapshot child: snapshot.getChildren()) {
Log.i("MyTag", child.getValue().toString());
imagesfeedsList.add(child.child("address").getValue(String.class));
authorfeedsList.add(child.child("author").getValue(String.class));
ratingfeedsList.add(child.child("rating").getValue(String.class));
locationfeedsList.add(child.child("location").getValue(String.class));
publicIDfeedsList.add(child.child("public_id").getValue(String.class));
}
Log.i("MyTag_imagesDirFinal", imagesfeedsList.toString());
mImages = imagesfeedsList.toArray(new String[imagesfeedsList.size()]);
author = authorfeedsList.toArray(new String[authorfeedsList.size()]);
ratingV = ratingfeedsList.toArray(new String[ratingfeedsList.size()]);
locationV = locationfeedsList.toArray(new String[locationfeedsList.size()]);
publicID = publicIDfeedsList.toArray(new String[publicIDfeedsList.size()]);
numbOfAdrs = Long.valueOf(imagesfeedsList.size());
LENGTH = Integer.valueOf(String.valueOf(numbOfAdrs));
}
right after the snippet the adapter setup;
ContentAdapter adapter = new ContentAdapter(recyclerView.getContext());
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
return recyclerView;
}
Then comes the view holder with a RecycleView, declaring the cardView elements. One of the elements is a ratingBar, and here where the ratingbar Listener is to submit the user rating on a specific picture.
after that the content adapter;
public static class ContentAdapter extends RecyclerView.Adapter<ViewHolder> {
// Set numbers of List in RecyclerView.
private Context mContext;
public ContentAdapter(Context context) {
this.mContext = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()), parent);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.authorName.setText(author[position]);
holder.ratingValue.setText(ratingV[position]);
holder.locationValue.setText(locationV[position]);
Picasso.with(mContext).load(mImages[position]).into(holder.picture);
#Override
public int getItemCount() {
return LENGTH;
}
}
My problem is whenever the user submits a rating or even when the data related to any of the elements on anycard changes, the view gets duplicated ( i mean by the view, the cards ), a repetition of the cards, with the new data chnages displayed ?
i a not sure what is in my above code structure causing this and how to fix this repetitions, i mean i need the cards to be updated with the new data but not duplicated?
All right, so the problem was that every time the data changes, in the onCreate the imagesFeedList, ratingFeedList, etc does not get rid of the old information stored in it from the initial build, so when the refresh happens triggered by onDataChange, the new information gets added to the previous information, which cause the view to repeat the cards, thus just at the beginning of onDataChange and before storing any information in the several feedLists, it must be cleared;
imagesfeedsList.clear();
authorfeedsList.clear();
ratingfeedsList.clear();
locationfeedsList.clear();
publicIDfeedsList.clear();
and by that i made sure the view does not repeat build up based on old information.

Popup window Label is not getting loaded

1.Trying to display exception message in popup window. Exception message is not appearing.
2.Eg: When i click button a popup window (Second fxml file) is to load with proper exception message in the label
3.Popup window is appearing, But the Label is not loading (Bold one --> ExceptionLabel.setText("Please enter Proper file path")) it says null pointer exception.
4.I am not sure what i missing. Same declared in FX:ID and also in second fxml file linked the main controller. Thanks in advance.
#FXML
public Label ExceptionLabel;
Stage PopupWindow = new Stage();
public void Buttonhandle(ActionEvent event) throws IOException {
try {
if(ESBOutboundFile!=null && OutputFile!=null){
String Output = SBlogpaser.Logpaser(ESBInboundFile,ESBOutboundFile,OutputFile);
System.out.println(Output);
}else{
Window(PopupWindow);
**ExceptionLabel.setText("Please enter Proper file path");**
}
} catch (Exception ex) {
System.out.println(ex);
}
}
public void Window(Stage Popup) throws Exception {
this.Popup=Popup;
final FXMLLoader fxmlLoader = new FXMLLoader();
Parent root= fxmlLoader.load(getClass().getResource("POPUPWindow.fxml"));
Scene scene1 = new Scene(root);
Popup.setScene(scene1);
Popup.show();
}
If i keep the label in "OK" handle button it is getting displayed.
From where are you expecting ExceptionLabel to be instantiated?
Assuming you are pointing the fx:controller attribute of the root of your POPUPWindow.fxml file to the current class, it will just create a new instance of that class, and inject values into that instance. The field ExceptionLabel in the current instance won't be initialized.
You could probably just about make this work by setting the controller of the FXMLLoader to the current object, something like this:
public void window(Stage popup) throws Exception {
this.popup=popup; // why?
final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("POPUPWindow.fxml"));
fxmlLoader.setController(this);
Parent root= fxmlLoader.load();
Scene scene1 = new Scene(root);
popup.setScene(scene1);
popup.show();
}
and then remove the fx:controller attribute from POPUPWindow.fxml.
This seems like a really bad idea, though, because now the current object is acting as a controller for two different FXML files. This will be confusing at best, and under fairly reasonable conditions would produce weird results. It would be much better to write a different controller class for the popup:
public class PopupController {
private final String message ;
#FXML
private Label exceptionLabel ;
public PopupController(String message) {
this.message = message ;
}
public void initialize() {
exceptionLabel.setText(message);
}
}
and then use the window(...) method above, but with
fxmlLoader.setController(new PopupController("Please enter Proper file path"));
Obviously, if you're reusing the window(..) method, you might want to pass the message in as a parameter to that method.

SmartGWT - Cannot change configuration property "x" after the component created

From this example http://www.smartclient.com/smartgwt/showcase/#tree_databinding_local , I started to implement my own tree structure dynamically (TreeGrid). When I try to render it, I get this error (title).
public class ProjectTreeGridScreen extends Screen {
Tree tree;
#Override
protected void onLoad() {
super.onLoad();
TreeGrid treeGrid = new TreeGrid();
setPageTitle(Util.C.projectListTitle());
treeGrid.setWidth(600);
treeGrid.setHeight(400);
TreeGridField projectTree = new TreeGridField("ProjectName", "Project Tree");
TreeGridField projectPath = new TreeGridField("ProjectPath", "Complete path");
TreeGridField projectDescription = new TreeGridField("ProjectDescription", "Description");
TreeGridField projectInfo = new TreeGridField("ProjectInfo", "Information");
treeGrid.setFields(projectTree, projectPath, projectDescription, projectInfo);
treeGrid.setData(tree);
add(treeGrid);
}
#Override
protected void onInitUI() {
super.onInitUI();
tree = new Tree();
tree.setModelType(TreeModelType.PARENT);
tree.setNameProperty("ProjectName");
tree.setIdField("ProjectItem");
tree.setParentIdField("ProjectParent");
tree.setShowRoot(true);
populateProjects();
}
protected void populateProjects() {
Util.PROJECT_SVC.visibleProjects(
new ScreenLoadCallback<List<Project>>(this) {
#Override
public void preDisplay(final List<Project> result) {
tree.setData(ProjectTreeGridBuilder.fromRepositories(result));
}
});
}
}
what do you mean by "x"? Normally, If a component has been drawn on the window (implicit or explicit call to draw), you cannot change it's properties values. So the only possible solution is to recreate the object with the new X value each time it is changing.

Resources