I need to save the selection status of multiple RadioButtons, so I can see which RadioButton is selected, when I go back to the scene later on. It's not about the userData, it's about to see whether it's selected. Right now I know how to make it work but with a lot of messy copy & paste. Something like this for every ToggleGroup:
#FXML private RadioButton rb1;
#FXML private RadioButton rb2;
public static int[] status = new int[600];
// to save it
if (rb1.getSelect(true)){
status[0] = 1;
} else {
status[0] = 0;
}
// to load it
if (status[0] == 1){
rb1.setSelected(true);
} else {
rb2.setSelected(true);
}
The problem is that I program a survey with more than 300 questions with binary answers. So I have more than 600 different RadioButtons. It'd take hours to implement it this way.
Is there any smart way to do it? I'm grateful for any advice. Thanks in advance!
Here is a SCVExample, that contains the simplest implementation based on my comment: It defines a model (Survey and Question) then binds the GUI to this model.
public class Radios extends Application {
class Survey {
private ObservableList<Question> questions = FXCollections.observableArrayList();
public ObservableList<Question> getQuestions() {
return questions;
}
}
class Question {
private StringProperty text = new SimpleStringProperty("");
private BooleanProperty answer = new SimpleBooleanProperty(false);
public Question(String text) {
setText(text);
}
public boolean isAnswer() {
return answer.get();
}
public BooleanProperty answerProperty() {
return answer;
}
public void setAnswer(boolean answer) {
this.answer.set(answer);
}
public String getText() {
return text.get();
}
public StringProperty textProperty() {
return text;
}
public void setText(String text) {
this.text.set(text);
}
}
#Override
public void start(Stage primaryStage) throws Exception {
// Model
Survey survey = new Survey();
for (int i = 0; i<300; i++) {
Question question = new Question("Do you like number " + i + "?");
question.answerProperty().addListener((obs, oldval,newval) -> {
System.out.println("Question: " + question.getText() + " answer changed from " + oldval + " to " + newval);
});
survey.getQuestions().add(question);
}
// View
VBox root = new VBox();
root.setSpacing(10);
for (Question question : survey.getQuestions()) {
VBox vBox = new VBox();
vBox.setSpacing(5);
HBox answerHBox = new HBox();
answerHBox.setSpacing(20);
vBox.getChildren().addAll(new Label(question.getText()), answerHBox);
RadioButton yes = new RadioButton("Yes");
RadioButton no = new RadioButton("No");
ToggleGroup toggleGroup = new ToggleGroup();
yes.setToggleGroup(toggleGroup);
no.setToggleGroup(toggleGroup);
answerHBox.getChildren().addAll(yes, no);
yes.setSelected(question.isAnswer());
no.setSelected(!question.isAnswer());
toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
question.setAnswer(newValue.equals(yes));
});
root.getChildren().add(vBox);
}
Scene scene = new Scene(new ScrollPane(root), 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This will generate a survey like:
Console output:
Question: Do you like number 1? answer changed from false to true
Question: Do you like number 3? answer changed from false to true
Question: Do you like number 6? answer changed from false to true
Question: Do you like number 8? answer changed from false to true
Question: Do you like number 8? answer changed from true to false
Question: Do you like number 8? answer changed from false to true
Related
Here I receive an ObservableList of Products. Then for each Product in the List I create a Label for the name, price and quantity. I also create two Buttons, to add and remove quantity, altering the value in the label. My problem is in the ´escolherProdutos()´ method, where I want to access the value of each Label respective to de quantity so I can know what quantities of each product are being requested, and I cant seem to access these values from outside the populateFlowPane() method. This is probably not the best solution to my problem, and I am a beginner, so if you can help me solve my problem or even have a better way of doing this I would be really grateful.
public class EscolherProdutosController
{
#FXML private VBox nomesVBox;
#FXML private VBox precoVBox;
#FXML private VBox qtdsVBox;
#FXML private Button escolherBtn;
private static ArrayList<Label> quantidades = new ArrayList<>();
#FXML
public void initialize()
{
populateFlowPane();
}
public void populateFlowPane()
{
ObservableList<Produto> produtos = Logic.getProdutos();
produtos.forEach(prod -> {
HBox hbox = new HBox(5);
Label nome = new Label(prod.getNome());
Label preco = new Label(String.valueOf(prod.getPreco()));
Button minus = new Button("-");
minus.setMinSize(20, 20);
Label qtd = new Label("0");
Button plus = new Button("+");
nomesVBox.getChildren().add(nome);
precoVBox.getChildren().add(preco);
hbox.getChildren().addAll(minus, qtd, plus);
qtdsVBox.getChildren().add(hbox);
//remover unidades do produto
minus.setOnAction((ActionEvent e) -> {
int quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 1)
{
quantidade--;
qtd.setText(String.valueOf(quantidade));
}
});
//adicionar unidades do produto
plus.setOnAction((ActionEvent e) -> {
int quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 0)
{
quantidade++;
qtd.setText(String.valueOf(quantidade));
}
});
quantidades.add(qtd);
});
}
public void escolherProdutos()
{
ObservableList<Produto> produtos = Logic.getProdutos();
produtos.forEach(prod -> {
quantidades.forEach(qtd -> {
Logic.escolherProdutos(prod.getIdProduto(),
Integer.parseInt(qtd.getText()));
});
});
}
}
I would probably add them to a list and access them accordingly im not sure what you need from them because you are being vague but take a look at the code sample below
public class MySceneController {
#FXML private ListView listView;
private ArrayList<Label> labelList = new ArrayList<>();
#FXML
public void initialize()
{
populateListView();
}
public void populateListView()
{
ObservableList<Products> products = Logic.getProducts();
products.forEach(prod -> {
Label label = new Label(prod.getName());
//Add them to a list here
labelList.add(label);
listView.getItems().addAll(results);
});
}
public void doSomething()
{
for (Label label : labelList) {//Maybe iterate through the list depending on what you need
//do something
}
//Here is where I need to access the label values
}
}
I solved the problem. Maybe it's not the best solution, but it works for me. What I did was create a HashMap where the product ID is the key and the quantity is the value.
Then for the add and remove buttons, I simply replace the value for the corresponding key in the HashMap. Doing this, I can then use it as input for the escolherProdutos() method from the Logic class, to select the products.
public class EscolherProdutosController
{
#FXML private VBox nomesVBox;
#FXML private VBox precoVBox;
#FXML private VBox qtdsVBox;
private HashMap<BigDecimal, Integer> quantidades = new HashMap<>();
#FXML
public void initialize()
{
populateFlowPane();
}
public void populateFlowPane()
{
ObservableList<Produto> produtos = Logic.getProdutos();
produtos.forEach(prod -> {
HBox hbox = new HBox(5);
Label nome = new Label(prod.getNome());
Label preco = new Label(String.valueOf(prod.getPreco()));
Button minus = new Button("-");
minus.setMinSize(20, 20);
Label qtd = new Label("0");
Button plus = new Button("+");
nomesVBox.getChildren().add(nome);
precoVBox.getChildren().add(preco);
hbox.getChildren().addAll(minus, qtd, plus);
qtdsVBox.getChildren().add(hbox);
//remover unidades do produto
minus.setOnAction((ActionEvent e) -> {
Integer quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 1)
{
quantidade--;
qtd.setText(String.valueOf(quantidade));
if(quantidades.containsKey(prod.getIdProduto()))
quantidades.replace(prod.getIdProduto(), quantidade);
else
quantidades.put(prod.getIdProduto(), quantidade);
}
});
//adicionar unidades do produto
plus.setOnAction((ActionEvent e) -> {
Integer quantidade = Integer.parseInt(qtd.getText());
if(quantidade >= 0)
{
quantidade++;
qtd.setText(String.valueOf(quantidade));
if(quantidades.containsKey(prod.getIdProduto()))
quantidades.replace(prod.getIdProduto(), quantidade);
else
quantidades.put(prod.getIdProduto(), quantidade);
}
});
});
}
public void escolherProdutos()
{
Logic.escolherProdutos(quantidades);
}
I have to use a component which has autocomplete and multiselection, I will attach an image to show what I mean:
I know it is not supported by the base JavaFx but maybe you know where can I find any suggestion how to do it.
If there is any 3rd party library which has this functionality I would appreciate a link, or if doesn't then any suggestion / idea which helps me implementing it.
The autocomplete part is already implemented and answered here: JavaFX TextField Auto-suggestions so please don't suggest it. I'm interested in the multiselection part so after an element is found to be displayed in the textfield and I can look for further items.
Here is the solution which combines both the autocomplete and tagbar property.
public class AutocompleteMultiSelectionBox extends HBox {
private final ObservableList<String> tags;
private final ObservableSet<String> suggestions;
private ContextMenu entriesPopup;
private static final int MAX_ENTRIES = 10;
private final TextField inputTextField;
public AutocompleteMultiSelectionBox() {
getStyleClass().setAll("tag-bar");
getStylesheets().add(getClass().getResource("style.css").toExternalForm());
tags = FXCollections.observableArrayList();
suggestions = FXCollections.observableSet();
inputTextField = new TextField();
this.entriesPopup = new ContextMenu();
setListner();
inputTextField.setOnKeyPressed(event -> {
// Remove last element with backspace
if (event.getCode().equals(KeyCode.BACK_SPACE) && !tags.isEmpty() && inputTextField.getText().isEmpty()) {
String last = tags.get(tags.size() - 1);
suggestions.add(last);
tags.remove(last);
}
});
inputTextField.prefHeightProperty().bind(this.heightProperty());
HBox.setHgrow(inputTextField, Priority.ALWAYS);
inputTextField.setBackground(null);
tags.addListener((ListChangeListener.Change<? extends String> change) -> {
while (change.next()) {
if (change.wasPermutated()) {
ArrayList<Node> newSublist = new ArrayList<>(change.getTo() - change.getFrom());
for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
newSublist.add(null);
}
for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
newSublist.set(change.getPermutation(i), getChildren().get(i));
}
getChildren().subList(change.getFrom(), change.getTo()).clear();
getChildren().addAll(change.getFrom(), newSublist);
} else {
if (change.wasRemoved()) {
getChildren().subList(change.getFrom(), change.getFrom() + change.getRemovedSize()).clear();
}
if (change.wasAdded()) {
getChildren().addAll(change.getFrom(), change.getAddedSubList().stream().map(Tag::new).collect(
Collectors.toList()));
}
}
}
});
getChildren().add(inputTextField);
}
/**
* Build TextFlow with selected text. Return "case" dependent.
*
* #param text - string with text
* #param filter - string to select in text
* #return - TextFlow
*/
private static TextFlow buildTextFlow(String text, String filter) {
int filterIndex = text.toLowerCase().indexOf(filter.toLowerCase());
Text textBefore = new Text(text.substring(0, filterIndex));
Text textAfter = new Text(text.substring(filterIndex + filter.length()));
Text textFilter = new Text(text.substring(filterIndex,
filterIndex + filter.length())); //instead of "filter" to keep all "case sensitive"
textFilter.setFill(Color.ORANGE);
textFilter.setFont(Font.font("Helvetica", FontWeight.BOLD, 12));
return new TextFlow(textBefore, textFilter, textAfter);
}
/**
* "Suggestion" specific listners
*/
private void setListner() {
//Add "suggestions" by changing text
inputTextField.textProperty().addListener((observable, oldValue, newValue) -> {
//always hide suggestion if nothing has been entered (only "spacebars" are dissalowed in TextFieldWithLengthLimit)
if (newValue.isEmpty()) {
entriesPopup.hide();
} else {
//filter all possible suggestions depends on "Text", case insensitive
List<String> filteredEntries = suggestions.stream()
.filter(e -> e.toLowerCase().contains(newValue.toLowerCase()))
.collect(Collectors.toList());
//some suggestions are found
if (!filteredEntries.isEmpty()) {
//build popup - list of "CustomMenuItem"
populatePopup(filteredEntries, newValue);
if (!entriesPopup.isShowing()) { //optional
entriesPopup.show(this, Side.BOTTOM, 0, 0); //position of popup
}
//no suggestions -> hide
} else {
entriesPopup.hide();
}
}
});
//Hide always by focus-in (optional) and out
focusedProperty().addListener((observableValue, oldValue, newValue) -> entriesPopup.hide());
}
/**
* Populate the entry set with the given search results. Display is limited to 10 entries, for performance.
*
* #param searchResult The set of matching strings.
*/
private void populatePopup(List<String> searchResult, String searchRequest) {
//List of "suggestions"
List<CustomMenuItem> menuItems = new LinkedList<>();
//Build list as set of labels
searchResult.stream()
.limit(MAX_ENTRIES) // Limit to MAX_ENTRIES in the suggestions
.forEach(result -> {
//label with graphic (text flow) to highlight founded subtext in suggestions
TextFlow textFlow = buildTextFlow(result, searchRequest);
textFlow.prefWidthProperty().bind(AutocompleteMultiSelectionBox.this.widthProperty());
CustomMenuItem item = new CustomMenuItem(textFlow, true);
menuItems.add(item);
//if any suggestion is select set it into text and close popup
item.setOnAction(actionEvent -> {
tags.add(result);
suggestions.remove(result);
inputTextField.clear();
entriesPopup.hide();
});
});
//"Refresh" context menu
entriesPopup.getItems().clear();
entriesPopup.getItems().addAll(menuItems);
}
public final ObservableList<String> getTags() {
return tags;
}
public final ObservableSet<String> getSuggestions() {
return suggestions;
}
/**
* Clears then repopulates the entries with the new set of data.
*
* #param suggestions set of items.
*/
public final void setSuggestions(ObservableSet<String> suggestions) {
this.suggestions.clear();
this.suggestions.addAll(suggestions);
}
private class Tag extends HBox {
Tag(String tag) {
// Style
getStyleClass().add("tag");
// Remove item button
Button removeButton = new Button("x");
removeButton.setBackground(null);
removeButton.setOnAction(event -> {
tags.remove(tag);
suggestions.add(tag);
inputTextField.requestFocus();
});
// Displayed text
Text text = new Text(tag);
text.setFill(Color.WHITE);
text.setFont(Font.font(text.getFont().getFamily(), FontWeight.BOLD, text.getFont().getSize()));
// Children position
setAlignment(Pos.CENTER);
setSpacing(5);
setPadding(new Insets(0, 0, 0, 5));
getChildren().addAll(text, removeButton);
}
}
}
.css
.tag-bar {
-fx-border-color: lightblue;
-fx-spacing: 3;
-fx-padding: 3;
-fx-max-height: 30;
}
.tag-bar .tag {
-fx-background-color: -fx-selection-bar;
-fx-border-radius: 5 5 5 5;
}
.tag-bar .tag {
-fx-text-fill: white;
}
.tag-bar .tag .button{
-fx-text-fill: orange;
-fx-font-weight: bold;
}
Adding to #Sunflame's answer (Sorry for Kotlin code)
Add this after the popup is created on the constructor, if you want to add a new item
inputTextField.onKeyTyped = EventHandler { event ->
if ("\r" == event.character && inputTextField.text.isNotEmpty()) {
val newTag = inputTextField.text
suggestions.add(newTag)
tags.add(newTag)
inputTextField.text = ""
}
}
Thank you for the hard work, this is just a minor feature added
Hello having trouble creating an array from a dialog box. I am able to type in the dialog and print out index [0] of ArrayList. But when I press Ok it restarts and replaces index [0] instead of adding to the ArrayList . How do I get the application to keep running instead of restarting?
Original Assignment:
Going back to the GUI assignment. You can modify this to use JavaFX
if you want to.
Create an application that has a button that has “Enter your info.”
When clicked it will present a JDialog that has labels and text boxes
that allow the user to enter their name, email, and phone number. The
JDialog will have buttons OK and Cancel.
When the user clicks Cancel, the dialog will go away without doing
anything else.
When the user clicks OK, the user’s information will be extracted from
the dialog, and dumped out on the console. Notice that you only are
listening for Click events. We will add to that spec.
Define a class to hold the info for a single person. Declare an
ArrayList of this type. When the user clicks OK, extract the info
from the dialog. This time, pass the info to the constructor method
of your class that will hold the info. Add this newly created object
to your array list.
Using an enhanced for loop, dump the ArrayList onto the console.
Add several user info's to the ArrayList, each time, dumping the
entire list to the console.
Sort the ArrayList. Dump the ArrayList onto the console.
public class UsefulGUI extends Application {
#Override
public void start(Stage primaryStage) {
Button button = new Button();
button.setText("Enter your info");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
dialogBox();
}
});
StackPane pane = new StackPane();
pane.getChildren().add(button);
Scene scene = new Scene(pane, 500, 300);
primaryStage.setTitle("Useful GUI");
primaryStage.setScene(scene);
primaryStage.show();
}
public class User {
private String name;
private String email;
private String phone;
public User(String n, String e, String p) {
this.name = n;
this.email = e;
this.phone = p;
}
#Override
public String toString() {
return "Name: " + name + " Email: " + email + " Phone: " + phone;
}
}
public void dialogBox() {
Dialog<User> dialog = new Dialog<>();
ArrayList<User>list = new ArrayList<User>();
dialog.setTitle("User info");
Label name = new Label("Name: ");
Label email = new Label("Email: ");
Label phone = new Label("Phone: ");
TextField textName = new TextField();
TextField textEmail = new TextField();
TextField textPhone = new TextField();
GridPane grid = new GridPane();
grid.add(name, 1, 1);
grid.add(textName, 2, 1);
grid.add(email, 1, 2);
grid.add(textEmail, 2, 2);
grid.add(phone, 1, 3);
grid.add(textPhone, 2, 3);
dialog.getDialogPane().setContent(grid);
ButtonType buttonType = new ButtonType("Ok", ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(buttonType, ButtonType.CANCEL);
dialog.setResultConverter(new Callback<ButtonType, User>() {
#Override
public User call(ButtonType b) {
if (b == buttonType) {
return new User(textName.getText(), textEmail.getText(), textPhone.getText());
}
return null;
}
});
Optional<User>info = dialog.showAndWait();
// Adds user to list
list.add(new User(textName.getText(), textEmail.getText(), textPhone.getText()));
System.out.print("Info Added.\n NEWUSER ");
System.out.println(new User(textName.getText(), textEmail.getText(), textPhone.getText()));
//Collections.sort(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(i + ". " + list.get(i));
}
//if (info.isPresent()) {
// System.out.println(info.get());
//}
}
public void quitApplication() {
Platform.exit();
}
public static void main(String[] args) {
launch(args);
}
}
I need to detect if a node is currently displaying.
I.e. if my Node is in a TabPane, I need to know if it is in a selected tab or not.
In the example, I want to know when the HBox is displaying.The visibleProperty and managedProperty of Node, does not seem to help me:
public class VisibleTest extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabpane = new TabPane();
tabpane.getTabs().add(new Tab("Tab1", new Label("Label1")));
HBox hbox = new HBox(new Label("Label2"));
hbox.setStyle("-fx-background-color: aquamarine;");
hbox.visibleProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Hbox visible changed. newValue: " + newValue);
});
hbox.managedProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("Hbox managed changed. newValue: " + newValue);
});
Tab tab2 = new Tab("tab2", hbox);
tabpane.getTabs().add(tab2);
primaryStage.setScene(new Scene(tabpane));
primaryStage.setWidth(600);
primaryStage.setHeight(500);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I know, it is possible to listen on the selectedProperty state of the tab, but this does not solve my real problem.
Node.impl_isTreeVisible() does what I want, but this is depricated API.
Any ideas?
------------------------------------ update--------------------
I realize the code example above does not explain well what I'm trying to accomplish. Below is some Swing code that kind of demonstrates what I am trying to accomplish in JavaFX. Detect if the JComponent/Node is visible/shown, and based on that state, start or stop background processes. How would the constructor look like if it was a javaFX class.
public class SwingVisible extends JComponent {
String instanceNR;
Thread instanceThread;
boolean doExpensiveStuff = false;
public SwingVisible(String instanceNR) {
this.instanceNR = instanceNR;
this.setLayout(new FlowLayout());
this.add(new JLabel(instanceNR));
instanceThread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (doExpensiveStuff) {
/*
* do expensive stuff.
*/
System.out.println(instanceNR + " is visible " + isVisible());
}
}
}
});
/*
* How to do this in FX?
*/
addComponentListener(new ComponentAdapter() {
#Override
public void componentShown(ComponentEvent e) {
if (!instanceThread.isAlive()) {
instanceThread.start();
}
doExpensiveStuff = true;
}
#Override
public void componentHidden(ComponentEvent e) {
doExpensiveStuff = false;
}
});
}
public static void main(String[] args) {
/*
* This block represents code that is external to my library. End user
* can put instances of SwingVisible in JTabbedPanes, JFrames, JWindows,
* or other JComponents. How many instances there will bee is not in my
* control.
*/
JTabbedPane jtp = new JTabbedPane();
jtp.add("tab1", new SwingVisible("1"));
jtp.add("tab2", new SwingVisible("2"));
jtp.add("tab3", new SwingVisible("3"));
JFrame f = new JFrame("test");
f.setContentPane(jtp);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Output when tab1 is selected:
1 is visible true
1 is visible true
1 is visible true
...
Output when tab2 is selected:
2 is visible true
2 is visible true
2 is visible true
...
You can use Tab's selectedProperty to know if it is selected or not, and by extension if its content is visible or not. It is a boolean property.
I've converted your Swing code to JavaFX based on your initial JavaFX example:
public class VisibleTest extends Application {
public class FXVisible extends Tab {
FXVisible(String id) {
super(id, new Label(id));
Timeline thread = new Timeline(
new KeyFrame(Duration.ZERO, e -> {
if (isSelected()) {
// do expensive stuff
System.out.println(id + " is visible");
}
}),
new KeyFrame(Duration.seconds(1))
);
thread.setCycleCount(Timeline.INDEFINITE);
selectedProperty().addListener((selectedProperty, wasSelected, isSelected) -> {
if (isSelected) {
if (thread.getStatus() != Status.RUNNING) {
System.out.println(id + " starting thread");
thread.play();
}
}
// else, it is not selected -> content not shown
});
}
}
#Override
public void start(Stage primaryStage) throws Exception {
TabPane tabpane = new TabPane();
tabpane.getTabs().add(new FXVisible("1"));
tabpane.getTabs().add(new FXVisible("2"));
tabpane.getTabs().add(new FXVisible("3"));
// add as many as you want
primaryStage.setScene(new Scene(tabpane));
primaryStage.setWidth(600);
primaryStage.setHeight(500);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I replaced your thread with a JavaFX Timeline. Your question is not about this topic so I won't go into details here, though it's self explanatory.
I don't understand why in the Swing example you have a listener changing a boolean that indicates if the component is visible or not when you can just call isVisible() directly in the thread (see comments below for a note about threading). This is why in my code above I took the approach of checking isSelected() directly with no self-declared boolean. If you need to revert to your design it's rather straightforward. Just noting this for clarity.
The ComponentListener can be replaced with a change listener on selectedProperty() and querying the new value. Just be sure that your example does what it's supposed to do: the first time the tab is selected the thread/timer starts. After that the thread/timer does nothing. You might have wanted to pause the computation for non-displaying content. Again, just noting it because it seemed like a potential mistake to me, otherwise you're fine.
Updated answer.
tab2.getContent().isVisible();
It seems to me that my original answer is correct. If not, you need to ask your question in a better way. You want to know when the hbox is visible(meaning you can see the hbox on the screen).
tabpane.getSelectionModel().selectedItemProperty().addListener((obsVal, oldTab, newTab)->{
System.out.println(newTab.getText());
if(newTab.getText().equals("tab2"))
{
//You can use this code to set the hbox visibility, that way you can force the behavior you are looking for.
hbox.setVisible(true);
System.out.println("hbox is visible!");
}
else
{
//You can use this code to set the hbox visibility, that way you can force the behavior you are looking for.
hbox.setVisible(false);
System.out.println("hbox is not visible!");
}
});
From quick checking this seemed to work for both checking the window is showing and that the tab it is in is displaying. I have also checked and it seems to work as expected for titled panes too that are collapsible.
public static boolean detectVisible( Node node )
{
Node current = node;
while( current != null ) {
if( !current.isVisible() ) {
return false;
}
current = current.getParent();
}
Window window = Optional.of( node ).map( Node::getScene ).map( Scene::getWindow ).orElse( null );
if( window == null ) {
return false;
}
if( window instanceof Stage && ( (Stage) window ).isIconified() ) {
return false;
}
return window.isShowing();
}
I have been following an online tutorial on how to use JavaFX to create Java Gui programs.
This is one of the programs I made following using the tutorials. I decided to add a counter to the command line which would display the number of orders as well as the customers choices, although I was able to get this working the counter feature looks inefficient.
I feel adding another class to just add the orderNumber variable was wrong.
Is there any advice on streamlining this code?
Also what could I do to output the orderNumber after the Order?
public class Main extends Application {
private class Order {
public int orderNumber = 0;
}
Stage window;
Button button;
public static void main(String[] args) {
launch(args);
}
#Override
public void start (Stage primaryStage) throws Exception {
Order Number = new Order();
window = primaryStage;
window.setTitle("Luke's Sandwich's");
//Check Box's
CheckBox box1 = new CheckBox("cheese");
CheckBox box2 = new CheckBox("Bacon");
CheckBox box3 = new CheckBox("Tuna");
CheckBox box4 = new CheckBox("Tomatoes");
box1.setSelected(true);
//button
button = new Button("Order Now");
button.setOnAction(e -> {
Number.orderNumber++;
System.out.println("order: " + Number.orderNumber);
handleOptions(box1,box2,box3,box4);
});
VBox layout = new VBox(10);
layout.setPadding(new Insets(20));
layout.getChildren().addAll(box1,box2,box3,box4,button);
Scene scene = new Scene(layout,300,250);
window.setScene(scene);
window.show();
}
//Handle checkbox options
public void handleOptions (CheckBox box1,CheckBox box2,CheckBox box3,CheckBox box4){
String message = "user order:\n";
if(box1.isSelected())
message += "cheese\n";
if(box2.isSelected())
message += "bacon\n";
if(box3.isSelected())
message += "tuna\n";
if(box4.isSelected())
message += "tomatoes\n";
System.out.println(message);
}
}