Proper way to add nodes using loop - javafx

I'm trying to make a small application that displays the contents of an arrayList but I have not been succesful. Currently I have a loop concatenating each object in the list with their toString method. This is not the solution I want for displaying the arrayList however. I want to be able to add a separate label and button for each object in the list so that I can press the button and open a window to change the item's data. Is there a way to add multiple javafx nodes using a loop AND add a parameter/id to a button? I know you can set an id to a button using fxml but I have to make this application without using fxml and have not been able to figure out how to do it otherwise.
Here is a code example of how I add the text right now
String list;
for (Registration registration : registrationList) {
list += registration.toString();
}
label.setText(list);
and what I've tried so far
for (Registration registration : registrationList) {
Label dynamicLabel = new Label(registration.toString());
Button dynamicButton = new Button("" + registration.getId());
layout1.getChildren().addAll(dynamicLabel, dynamicButton);
}
please let me know if something is unclear in my question, thanks in advance.

The problem is that you're thinking about the controls like data, and you need to treat them like regular objects. You're also thinking about the entire thing monolithically, which makes it difficult to see a solution.
Think about a Label + a Button as a distinct unit that works together. Define them together and you can freely have them talk to each other without figuring out ways to identify them and link them together later on.
Something like this will do what you're looking for:
public class ButtonActionSample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
List<String> strings = List.of("String 1", "String 2", "String 3");
VBox vBox = new VBox(5);
vBox.getChildren().addAll(strings.stream().map(string -> createHBox(string)).collect(Collectors.toList()));
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
}
private Node createHBox(String string) {
Label label = new Label(string);
Button button = new Button("Change Text");
button.setOnAction(evt -> new TextInputDialog(label.getText()).showAndWait().ifPresent(label::setText));
return new HBox(5, label, button);
}
}
When you do it like this, you don't need to hang on to references to any of the screen elements at all; just create them, set the action on the Button and stick them on the screen. They just work after that.

Related

javafx reused pane initalization

Here's my situation. I have a javafx pane which I don't want to recreate each time I show it, because I want to keep all filled by user fields in between switching between views.
For that purpose I did this:
#FXML
private void initialize() {
createOrderPane = FxmlUtils.fxmlLoader(CREATE_ORDER_FXML);
}
public void setCenter(String fxmlPath) {
if(CREATE_ORDER_FXML.equals(fxmlPath)) {
borderPane.setCenter(createOrderPane);
}
else {
borderPane.setCenter(FxmlUtils.fxmlLoader(fxmlPath));
}
}
so in case user wants to see CREATE_ORDER_FXML it doesn't reload it, but uses already existing instance.
The problem is that some parts of the view should be reinitalized. For example database might change and I want to refresh some comboboxes which reads value from DB. How to achieve that?
Is there some onShow property? Or maybe I am able to get to the controller of createOrderPane object?

JavaFX Change list of labels on button click

I have a list of Labels in a JavaFX app that I've preset with a holder value as such:
for(int i = 0; i < 4; i++) {
lblUserNames.add(new Label("Username goes here"));
}
and I'm trying to change the label to display the username on the click of a button by using
public void setUsername(int index, String lblUserName) {
this.lblUserNames.set(index, new Label(lblUserName));
}
But it's still showing the holder text instead of updating to show the usernames; and yet when I print out the list of labels, the values have indeed changed.
I had it working before when I had just an array of labels (Label[]) and could use ".setText(lblUserName). I changed it to an ArrayList so that more users can be added and the code wouldn't have to change much, but now I can't use setText() anymore.
I've seen similar questions for changing the text for just a Label but the solution is to use setText() which won't work with an ArrayList of Labels. Is there any way to update and replace the holder text with the new labels for an ArrayList?
When you replace the label in your list, you are not replacing it in the UI. The original label is still shown in the UI. Just call setText(...) on the existing label:
public void setUsername(int index, String lblUserName) {
this.lblUserNames.get(index).setText(lblUserName);
}

JavaFX TreeView: remove expand / collapse button (disclosure node) & functionality

I want to have a TreeView that has all of its children permanently expanded, and I don't want the user to be able to expand or collapse any of the children.
To do this I've found that I need to do the following:
Remove icon with CSS (Done)
Change expand and collapse image TreeView JavaFX 2.2
[edit] Above link should be used to change image; to remove completely, use this solution: https://stackoverflow.com/a/27831191/4430591
Remove double click functionality (Done)
Disable TreeItem's default expand/collapse on double click JavaFX 2.2
[edit] Remove ability to collapse / expand using keyboard arrrow keys (Done)
Given in José Pereda's solution below ( https://stackoverflow.com/a/27831085/4430591 )
[edit] Remove ability to right click for a ContextMenu (Done)
Given in José Pereda's solution below ( https://stackoverflow.com/a/27831085/4430591 )
Remove icon's clickablity (How do I do this?)
[edit] solution: https://stackoverflow.com/a/27831191/4430591
Even though the icon is no longer visible, it's still clickable. I don't see any way of filtering this; I only see ways to be able to respond to it after the fact.
Also, if I'm missing anything else that I need to do to ensure this functionality, please let me know.
I feel quite silly. I think this was mostly just a matter of not knowing what that darn arrow was called. Apparently it's a disclosureNode? Maybe that's common knowledge.
In the custom defined TreeCell, all I did was add this line in the updateItem method:
setDisclosureNode(null);
The solution to avoid modifying the skin or the default behavior is more simple if we trap the clicks before they are dispatched, and consume the right ones.
For that we can use an EventDispatcher, to filter both the mouse pressed and the right click over the arrows, which are StackPane nodes:
class CellEventDispatcher implements EventDispatcher {
private final EventDispatcher original;
public CellEventDispatcher(EventDispatcher original) {
this.original = original;
}
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
if (event.getEventType().equals(MouseEvent.MOUSE_PRESSED) ||
event.getEventType().equals(ContextMenuEvent.ANY)){
event.consume();
}
if(event instanceof KeyEvent && event.getEventType().equals(KeyEvent.KEY_PRESSED)){
if((((KeyEvent)event).getCode().equals(KeyCode.LEFT) ||
((KeyEvent)event).getCode().equals(KeyCode.RIGHT))){
event.consume();
}
}
return original.dispatchEvent(event, tail);
}
}
Now we apply our custom dispatcher to the tree view:
#Override
public void start(Stage primaryStage) {
TreeView<String> tree = new TreeView<>();
...
EventDispatcher treeOriginal = tree.getEventDispatcher();
tree.setEventDispatcher(new CellEventDispatcher(treeOriginal));
Scene scene = new Scene(tree);
primaryStage.setScene(scene);
primaryStage.show();
}
This will consume any click (left or right) over the arrows on the tree.
EDIT
Added to the event dispatcher class the case where the user uses the keyboard to traverse the tree view, consuming the collapse/expand events with arrow LEFT or RIGHT.

Javafx : Activate a tooltip with a button

I'm using JavaFx for a little app and a want to display a tooltip on a textArea when the user is clicking on a "help" button.
No problem for linking a tootltip to my textArea, but no way to activate it when the user click on the button. Is there a way to do that?
This is what you are looking for:
final Button helpButton = new Button("Help");
helpButton.setOnAction(new EventHandler()
{
public void handle(Event arg0)
{
showTooltip(stage, helpButton, "test tool tip", null);
}
});
public static void showTooltip(Stage owner, Control control, String tooltipText,
ImageView tooltipGraphic)
{
Point2D p = control.localToScene(0.0, 0.0);
final Tooltip customTooltip = new Tooltip();
customTooltip.setText(tooltipText);
control.setTooltip(customTooltip);
customTooltip.setAutoHide(true);
customTooltip.show(owner, p.getX()
+ control.getScene().getX() + control.getScene().getWindow().getX(), p.getY()
+ control.getScene().getY() + control.getScene().getWindow().getY());
}
Just pass the button as an input instead of control.
The ability to display a Tooltip on demand requires a resolution of RT-19538 Customizable visibility timing for Tooltip, which is not implemented in JavaFX 2.2.
As a workaround, you could try any of the possible strategies below:
Displaying your Tooltip data in a ContextMenu instead. With a ContextMenu, you have complete control over when it is shown.
You could create a custom PopupControl for your required functionality.
You could replace the default TooltipSkin with a custom implemented skin which allows you to control when the Tooltip is displayed.
You could implement RT-19538 and provide a patch to the Tooltip and TooltipSkin to the openjfx project.
3rd party libraries such as Jide's JavaFX Beta Release provide special classes like Decorator utilities, IntelliHints and ShapedPopups which might be useful in your case.
The following show a tooltip over control.
If controlhas a Tooltip assigned to, this tooltip is not chnaged.
public static void showOneTimeTooltip(Control control, String tooltipText) {
Point2D p = control.localToScreen(5 , 5);
final Tooltip customTooltip = new Tooltip(tooltipText);
customTooltip.setAutoHide(false);
customTooltip.show(control,p.getX(),p.getY());
PauseTransition pt = new PauseTransition(Duration.millis(2000));
pt.setOnFinished(e->{
customTooltip.hide();
});
pt.play();
}

How do you add a row listener to a Flextable in GWT?

How do you add a row listener to a specific row, or all rows in a table? I need to add a type of "onMouseOver" listener to the rows so that when you hover over them, it changes the background color of the row, much like getRowFormatter will allow you to do.
// this is click
final FlexTable myTable = new FlexTable();
myTable.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
Cell cell = myTable.getCellForEvent(event);
int receiverRowIndex = cell.getRowIndex(); // <- here!
}
});
Supposing GWT 1.5.3:
CLICK EVENT HANDLING
If you are using FlexTable and you wanted a click event handler, you could use the FlexTable.addTableListener() and register your own TableListener. The TableListener object will need to implement the onCellClicked callback which would give you the row number.
OTHER EVENTS HANDLING (e.g. HOVER)
If you need to handle other type of events other than click (say, hover), GWT currently doesn't have a ready interface for that. You pretty much left on your own to implement them yourself. There's two ways of doing it that I can think of now:
The quick and dirty way, is probably by exploiting JSNI, which provides a means for you to inject Javascript into your GWT code. I didn't use much JSNI (apart from really hard workarounds which is not worth the effort writing it in pure GWT) in my code so I can't show you an example; but frankly I won't recommend this as it reduces maintainability and extensibility.
If you wanted a native, GWT interface, you can create a new class that inherits HTMLTable or FlexTable. At the constructor, call the sinkEvents function with your needed events. (e.g. for hover, you'll probably need sinkEvents(Event.ONMOUSEOVER)). Then you'll need the onBrowserEvent function that handles the mouseover.
A quick template of how the code should look like:
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlexTable;
public class FlexTableWithHoverHandler
extends FlexTable
{
public FlexTableWithHoverHandler()
{
super();
sinkEvents(Event.ONMOUSEOVER);
}
#Override
public void onBrowserEvent(Event event)
{
switch(DOM.eventGetType(event))
{
case Event.ONMOUSEOVER:
// Mouse over handling code here
break;
default:
break;
}
}
}
The best of learning how to code this is by looking at the GWT source code itself (search for sinkEvent) and getting the feel on how to do it the GWT way.
I found it much simple to add javascript directly to the TR element. My code assumes that a widgets DOM parent is a TD and the grandparent is the TR so you need to be sure you know your DOM.
Here's my code. Nice and simple, no JSNI or GWT DOM event management required.
TableRowElement rowElement = (TableRowElement) checkbox.getElement().getParentElement().getParentElement();
rowElement.setAttribute("onMouseOver", "this.className='" + importRecordsResources.css().normalActive() + "'");
rowElement.setAttribute("onMouseOut", "this.className='" + importRecordsResources.css().normal() + "'");
I just did it this simple way:
protected void handleRowsSelectionStyles(ClickEvent event) {
int selectedRowIndex = fieldTable.getCellForEvent(event).getRowIndex();
int rowCount = fieldTable.getRowCount();
for (int row = 0; row < rowCount; row++) {
Element rowElem = fieldTable.getRowFormatter().getElement(row);
rowElem.setClassName(row == selectedRowIndex ? "row selected" : "row");
}
}
You call this method from the cells you want to be clickable
int row = 0;
for (final RowDataProxy rowData : rowDataList) {
Label fieldName = new Label(rowData.name());
fieldName.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
handleRowsSelectionStyles(event);
}
});
fieldTable.setWidget(row++, 0, fieldName);
}
Best Regards,
Zied Hamdi

Resources