Button fooBar = new Button("1");
Button zooBar = new Button("2");
for (Node child : scene.lookupAll("*")) {
child.getStyleClass().add(child.getClass().getName());
}
I have a lot of Nodes and all of them need individuals styling. Now instead of repeating: Node.getStyleClass().add() the entire time I thought I would loop through all nodes and apply a style class with the same name as the variable. However, how exactly can I do this?
Edit: How do I reduce the amount of styleClass adds? Right now I need to do it for every node. I have 12 nodes (Hboxes, buttons etc) and its constantly growing which means that with the instantiation (new button etc) + the adding of stylesheets I have crazy amount of code.
Edit 2: Maybe my approach was wrong. Trying to create the gui programmatically wasnt a good idea. Im thinking of using fxml for complex guis now.
Related
My goal is to have a program with 3 panes. A mulitfactor Auth. The first pane will have the user type in a passphrase, while the second pain will allow the user to pick a image from a drop down list. But I want the 3rd pane to launch just to the right of the 2nd pane after the use selects a image in the same "Main" stage.
Not looking for someone to code a program just point me in the right direction to what im trying to do. My searching skills are failing, either im not explaining it right or theres another word for this.
Edit:
This is my idea of how i want it to work. Now that i look at it using a border pane probably makes since, But im still stuck with, How can I launch each section of the border at a different time, i.e when something is clicked.
I would go about it by having 3 panes side by side and just blank for the first FXML file you load in. I would then have another FXML file with the same layout that contains what you want to show up in those panes.
Then with that, you can have the controller on request (like when a user hits submit or however you are wanting these to show up) grab the content inside of the pane on the second FXML file by ID and load it into the pane.
I've done something similar with changing anchor panes and keeping the toolbar from the original so I can add more on this when I get home and should be able to supply some code that is modified to fit your issue.
Edit 1: Sorry I was in a hurry to submit that dive I had to go but I am on mobile now so I can edit but not able to add a lot, just felt I needed to say, there are different options for what you can use to do this which is why I just said a pane instead of anything specific. Just wanted to submit something so you can start looking in the right direction till I am able to update.
Edit 2: Alright now that I am home I tried this out and was able to get this working. Here is how I did it.
So I had two FXML files. One with the 3 areas that you have your items, however, only the box that you want to show when it starts is shown. Each area is enclosed by an AnchorPane. I used the AnchorPane as a container so I can swap out what is inside of it. I then had a second FXML file that had all of the boxes you want to show all of which enclosed in AnchorPanes. Here are pictures explaining what I mean.
I have the first pane named initial.fxml and the second named grabfrom.fxml. For the pane names, I just have it as pane1, pane2, and pane3. Lastly, the methods I have are show2() and show3() and call them from the FXML when the respective buttons are clicked inside of the AnchorePanes.
With initial, I just load that up as normal from the start method in my main class and that is all that is needed to be done with that. We only had it so we could display something that does not have the boxes showing before needed.
Now for the important part
With what I have in show2(), which is called when the button inside of the first pane (which is there from the start) is pressed.
public void show2() throws IOException{
AnchorPane toSetPane2=(AnchorPane) FXMLLoader.load(getClass().getResource("grabfrom.fxml"));
toSetPane2=(AnchorPane) toSetPane2.lookup("#pane2");
pane2.getChildren().setAll(toSetPane2);
}
What this is doing is loading the grabfrom.fxml into a temp var that we cast to an anchor pane. (Do note that this works since as you can see in the screenshot the whole FXML file is an anchor pane. If you're not using it that way you can take out the casting and cast to something you are using or not even cast depending on what it is.)
It then set the var we just made to just the AnchorPane we need, which is the second one since that's the one we are adding. It does this with the .lookup("#ID"); method to get just the pane we need.
Lastly, it sets everything inside of the current pane2 to toSetPane2.
This could all be compressed down into one line, however, I have left it as is for easier reading.
You should be able to use this method of loading in a portion of your application for loading in the third one and for that matter any other parts you want to in any situation.
Edit 3:
Also as #Swatarianess had said, there are stackpanes, this method will work with anything that you can set an ID to so they would work just as well. I used AnchorPanes because I have done a fair bit with them and had some code I could recycle whilst making a test for it so it was easier. All you would do if you were using those though is just cast to a StackPane instead of an AnchorPane like this:
public void show2() throws IOException{
StackPane toSetPane2=(StackPane) FXMLLoader.load(getClass().getResource("grabfrom.fxml"));
toSetPane2=(StackPane) toSetPane2.lookup("#pane2");
pane2.getChildren().setAll(toSetPane2);
}
The transition between panes could be done with a stackpane.
I'm developing a small GUI using JavaFX for an assignment. Having never used JavaFX before, most of what I've used or learned I've research myself. Most of my application runs smoothly, animations included.
I have a small user input section:
I'm trying to allow it to swipe left prior to loading the next scene, to give the user the impression of sequence. I achieved this using a ParallelTransition populated by TranslationTransitions in which I load all of these Nodes.
However, just slightly before it animates, it appears to align everything right.
I have a feeling this is something to do with the container (AnchorPane) in which I'm placing these objects, or some undefined attribute which I'm not setting. As I'm learning this all on the fly, it's a bit difficult to narrow down. Can anyone give me an idea as to what is causing this and how I can retain the alignment? Each Node moves in the following way:
translateTransition.setFromX(node.getLayoutX());
translateTransition.setToX(node.getLayoutX()-500.00);
If I've left out any pertinent information, please let me know!
So, my issue turned out to be the way I'm laying out my objects. Placing them in an arbitrary AnchorPane with only specific attributes to locate those items means that, upon animation, it will group those objects and animate them accordingly. It was the incorrect way to layout those nodes.
Instead, I've placed them in a VBox which has two advantages.
I can align the contents of that VBox centre, so I need not specify the exact layout attributes of the elements.
I can animate using the single VBox, as opposed to the all the elements in a ParallelTransition. As a result, a single TranslateTransition on the VBox node will suffice!
I need to build a View which has a 4 layer nested Multi-Accordion with a lot of checkboxes inside them. All together there might be around 30-40 Checkboxes all through the Accordions.
The next step will be, that i have some sort of logic behind all this. Depending on the selection combination of the checkboxes I will change a text label accordingly.
My thought process was: I put up all these checkboxes and give them a numeric fx:id representing there position in the nested accordion graph. Something like "1_1" or "2_4_1".
After that, I build one ChangeListener calling a Method on Selection of a Checkbox. I can look up the Id of the checked box, look it up in my data (to see which Text belongs to it and if any rules interfer with other boxes) and handle the logic accordingly while putting the id and its text in a Map or List to keep it for later and to keep track of the checked boxes.
Now I came to know, that getting the fx:id isnt something JavaFX wants me to do. I cant deliver a custom id in custom property inside the FXML either (couldnt find anything regarding this).
I am now pretty much at the end of my knowledge (I did just start with JavaFX and have some basic Java knowledge) and it seems to me, that I tackle this topic from the wrong side.
My question is now: What would be a best practice to handle dozens of checkboxes and trigger logic in the code according to the box that was checked without writing a ChangeListener for every single Check Box leaving me with some (imo) ugly code all the way.
EDIT: I forgot to mention: I did achieve some sort of functional solution by writing a custom CheckboxChangeListener with a reference to the Element the addListener method was called on and using "getId()" on this reference. I came to know though, that this method references the css:id of the fxml element and not its fx:id and I am not quite sure if this is a proper way to go
You should look into databinding with javafx. For example:
CheckBox cb1 = new CheckBox("1");
CheckBox cb2 = new CheckBox("2");
BooleanProperty isCb1Selected = cb1.selectedProperty();
BooleanProperty isCb2Selected = cb2.selectedProperty();
Textfield foo = new TextField().visibleProperty().bind(isCb1Selected.and(isCb2Selected));
This would hide the textfield foo if atleast one of the checkboxes isn't selected.
You can find other examples here and here an oracle tutorial
I've been tasked with doing refactoring to a Java Swing application we have here that's pretty crufty. My job is to clean up the code and make it more dynamic as we're going to start using it with a second project and it needs to be able to adjust appropriately. There is a small portion of one window that contains
A button
A JFormattedTextField that is used to select dates.
A 3X4 table of JLabels that display data.
The person who originally wrote this simply used a GridBagLayout JPanel and then hardcoded everything, including the table's header and row label's and left empty JLabel's in the dynamic data position. When the dynamic information is received setText is called with the text data. Part of my refactoring will cause the entire table to be dynamic in dimension as well as content so I decided to make the table a sub-panel with a GridLayout and dynamically set the contents and dimensions with this piece of code:
public void updateInfoPanel(ArrayList rows) {
System.out.println("Updating Info Panel!");
//genericInfo is the new sub panel in question.
genericInfo.removeAll();
GridLayout layout = new GridLayout();
layout.setColumns(rows.get(0).length);
layout.setRows(rows.size());
genericInfo.setLayout(layout);
for(String[] row : rows) {
for(String element : row) {
genericInfo.add(new Label(element));
}
}
}
I have verified that this is only ever getting called one time per window creation but the entire window is now incredibly sluggish. It can take >5 seconds to respond to clicks in other parts of the frame that used to respond in fractions of a second. Does anyone know what would cause this? Is there something about GridLayouts that I don't understand?
Try calling this code on the EDT.
No it appears you understand GridLayouts. The problem is elsewhere, look at other code that you might have changed, and do some profiling to determine the true source of the slowdown.
Does anyone have experience with the prefuse graph toolkit? Is it possible to change an already displayed graph, ie. add/remove nodes and/or edges, and have the display correctly adapt?
For instance, prefuse comes with an example that visualizes a network of friends:
http://prefuse.org/doc/manual/introduction/example/Example.java
What I would like to do is something along the lines of this:
// -- 7. add new nodes on the fly -------------------------------------
new Timer(2000, new ActionListener() {
private Node oldNode = graph.nodes().next(); // init with random node
public void actionPerformed(ActionEvent e) {
// insert new node //
Node newNode = graph.addNode();
// insert new edge //
graph.addEdge(oldNode, newNode);
// remember node for next call //
oldNode = newNode;
}
}).start();
But it doesn't seem to work. Any hints?
You should be aware the several layers of prefuse:
Data
Visualization
Display
To be short, the three layers can be linked this way:
Graph graph = new Graph(eg. yourXML_file);
Visualization viz = new Visualization();
viz.add(GRAPH, graph);
Display disp = new Display();
disp.setVisualization(viz);
Display is a graphic component that you add to a panel as usual.
Here you only modify the data layer.
Node newNode = graph.addNode();
graph.addEdge(oldNode, newNode);
You need now to update the visual layer:
viz.run("repaint");
The repaint action has to be defined.
ActionList repaint = new ActionList();
repaint.add(new RepaintAction());
viz.putAction("repaint", repaint);
I really advise you to read the prefuse doc.
And you can find a lot a resources on the official forum
At least, I can say you that prefuse is for the moment not really efficient for live graph update.
But it should not be enough, as you modified the graph structure, you have to regenerate it in the visualization (ie. recalculate the node placements etc...). There are two actions already defined in your sample code. Run them at the end of your actionPerformed.
viz.run("color");
viz.run("layout");
This method is not very efficient, because it adds a lot of computation each time you add a node, but there are not any others for the moment with prefuse.
As pointed out in my other post, the reason new nodes and edges are not visible in the original example is that the colors etc. for the nodes are not set correctly. One way to fix this is to explicitly call vis.run("color"); whenever a node or edge was added.
Alternatively, we can ensure that the color action is always running, by initializing the ActionList to which we add it (called "color" in the original example) slightly differently:
instead of
ActionList color = new ActionList();
we could write
ActionList color = new ActionList(Activity.INFINITY);
This keeps the action list running indefinitely, so that new nodes/edges will automatically be initialized for their visual appearance.
However, it is unclear to me whether this would actually be the preferred method - for things like a dynamic layout action (e.g. ForceDirectedLayout), such a declaration makes perfect sense, but for colors it seems to me that a constantly running coloring action is mostly overhead.
So, perhaps the previously posted solution of just running the "color" action explicitly (but only once) whenever the graph gets extended, might be the better choice...
Okay, after digging a bit through the prefuse sources, I now have a better understanding of how things work under the hood. I found out that actually the new nodes I create with the code above are not only added correctly to the graph, the visualization also takes note of it!
So, unlike Jerome suggests, it is not necessary to call vis.run("layout") explicitly.
The reason I thought the nodes were not added correctly was the fact that they are drawn with white background-, border- and text color - on white background that is. Not astonishing that they are a bit difficult to spot.
To fix that one has to call the color action after a new node is inserted, like this:
// insert new edge //
graph.addEdge(oldNode, newNode);
vis.run("color"); // <- this is new
(Note that this action is defined further up in the code of Example.jar under //-- 4.)
One last thing I am unsure about now is whether calling this action will make prefuse go over all graph nodes again and set their color - for very large graphs that would be undesired, of course.
You need to tell the control container ('d', in example.java) do get redrawn. Calling invalidate() should be enough (not sure, though).
Anyway, this might help you.