I have this view designed via Scene Builder for JavaFX. There are 4 ComboBox in it. I would like to have the possibility to have something to let the user choose how many and which ComboBox use.
For example, my aim is having 3 modes:
allow the user to use all the 4 ComboBox;
allow the user to use only one ComboBox and let him choose it;
allow the user to use only two ComboBox and let them choose the preferred combination of the four Controls
Any design or idea (and its implementation) are well welcomed since I am not having a very good solution at this moment. I was thinking something like using the CheckBox element near to every ComboBox to enable or disable them, but anyway it is not very good. Also I was thinking about putting 3 Buttons to select the 3 modes and dynamically populate my Container, but I do not know where to start with the implementation.
If you want to let the user select a specific ComboBox, you can enable it using the JavaFX function setDisable() that is on all classes that inherit from the Node class.
(See difference between: setDisabled() vs setDisable())
In the case below, I bind the disabledProperty() to the inverse selectedProperty() on each CheckBox. This way you can select specific ComboBoxes to choose from. Hopefully this will get you started on seeing how JavaFX bindings work.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Test extends Application
{
public static void main(String[] args){
launch(args);
}
#Override
public void start (Stage primaryStage) throws Exception {
VBox vBox = new VBox();
HBox hBox1 = generateComboBoxHBox();
HBox hBox2 = generateComboBoxHBox();
HBox hBox3 = generateComboBoxHBox();
HBox hBox4 = generateComboBoxHBox();
vBox.getChildren().addAll(hBox1, hBox2, hBox3, hBox4);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
}
// Create 4 of the same HBoxes for an example. Each HBox has a checkbox and combobox
private HBox generateComboBoxHBox(){
HBox hBox = new HBox();
CheckBox checkBox = new CheckBox();
ComboBox<String> comboBox = new ComboBox<>(FXCollections.observableArrayList("Option1", "Option2", "Option3", "Option4"));
comboBox.disableProperty().bind(checkBox.selectedProperty().not());
hBox.getChildren().addAll(checkBox, comboBox);
return hBox;
}
}
Related
To get an idea of what I want
When the textfield is clicked, the dropdown appears with suggestions that are filtered out as the user types in the text field. The height of the box should also adjust real-time to either contain all of the items, or a maximum of 10 items.
I managed to get this somewhat working using a ComboBox, but it felt a bit rough around the edges and it didn't seem possible to do what I wanted (The dropdown doesn't resize unless you close it and re-open it).
New idea, have a text field and then show a VBox of buttons as the dropdown. The only problem is that I don't know how to position the dropdown so that it doest stay in the noral flow so it can overlay any exisiting elements below the text field. Any ideas?
Please consider this Example, you can take the idea and apply it to your project.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class SearchFormJavaFX extends Application{
#Override
public void start(Stage ps) throws Exception {
String[] options = {"How do I get a passport",
"How do I delete my Facebook Account",
"How can I change my password",
"How do I write some code in my question :D"};
// note that you don't need to stick to these types of containers, it's just an example
StackPane root = new StackPane();
GridPane container = new GridPane();
HBox searchBox = new HBox();
////////////////////////////////////////////////////
TextField text = new TextField();
// add a listener to listen to the changes in the text field
text.textProperty().addListener((observable, oldValue, newValue) -> {
if(container.getChildren().size()>1){ // if already contains a drop-down menu -> remove it
container.getChildren().remove(1);
}
container.add(populateDropDownMenu(newValue, options),0,1); // then add the populated drop-down menu to the second row in the grid pane
});
// those buttons just for example
// note that you can add action listeners to them ..etc
Button close = new Button("X");
Button search = new Button("Search");
searchBox.getChildren().addAll(text,close,search);
/////////////////////////////////////////////////
// add the search box to first row
container.add(searchBox, 0, 0);
// the colors in all containers only for example
container.setBackground(new Background(new BackgroundFill(Color.GRAY, null,null)));
////////////////////////////////////////////////
root.getChildren().add(container);
Scene scene = new Scene(root, 225,300);
ps.setScene(scene);
ps.show();
}
// this method searches for a given text in an array of Strings (i.e. the options)
// then returns a VBox containing all matches
public static VBox populateDropDownMenu(String text, String[] options){
VBox dropDownMenu = new VBox();
dropDownMenu.setBackground(new Background(new BackgroundFill(Color.GREEN, null,null))); // colors just for example
dropDownMenu.setAlignment(Pos.CENTER); // all these are optional and up to you
for(String option : options){ // loop through every String in the array
// if the given text is not empty and doesn't consists of spaces only, as well as it's a part of one (or more) of the options
if(!text.replace(" ", "").isEmpty() && option.toUpperCase().contains(text.toUpperCase())){
Label label = new Label(option); // create a label and set the text
// you can add listener to the label here if you want
// your user to be able to click on the options in the drop-down menu
dropDownMenu.getChildren().add(label); // add the label to the VBox
}
}
return dropDownMenu; // at the end return the VBox (i.e. drop-down menu)
}
public static void main(String[] args) {
launch();
}
}
What you're trying to do has already been implemented, and is included in ControlsFx. It's open source, and I think it would suit you need. It looks some what like this
You can even add custom nodes to it, so that cross can be done too.
public void pushEmails(TextField Receptient) {
ArrayList<CustomTextField> list = new ArrayList<>();
for (int i = 0; i < Sendemails.size(); i++) {
CustomTextField logo=new CustomTextField(Sendemails.get(i));
ImageView logoView=new ImageView(new Image("/Images/Gmail.png"));
logo.setRight(logoView);
list.add(logo);
}
TextFields.bindAutoCompletion(Receptient, list);
}
In my javafx application I want to create a scene where I can show the usernames of all the users in my database.
Precisely, I want to show a list of labels where every label get a username.
(the number of labels depend on the number of users).
Note: I can do this in java with a list and a foreach loop, but this is the first time that I work with javafx and I want to know how to create a loop of graphic component.
Thanks.
Here are a couple of alternatives, one sample is just looping and adding new labels to the children of a layout pane, the other is using the in-built ListView component. There are other alternatives of course. Which you choose to use will depend upon the functionality you need to achieve.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class UserDisplay extends Application {
#Override public void start(Stage stage) {
String[] users = { "Huey", "Dewey", "Louie" };
VBox layout = new VBox(10);
// ALTERNATIVE 1: add labels in a loop.
for (String user: users) {
Label userLabel = new Label(user);
layout.getChildren().add(userLabel);
}
// ALTERNATIVE 2: use the built-in ListView component.
ListView<String> listView = new ListView<>(
FXCollections.observableArrayList(users)
);
layout.getChildren().add(listView);
layout.setPadding(new Insets(10));
layout.setPrefSize(100,200);
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Basically, here is what I need:
I have a JavaFX ComboBox, and it is set to Editable. Since it is editable, there is a little text field in there where someone can enter in a String. I want to use previously generated data to populate that little text field. How do I accomplish this?
enterSchoolName.setSelectionModel((SingleSelectionModel<String>) FXCollections.observableArrayList(studentData.getSchoolName()));
This is all i have in the way of relevant code and an "attempt" at a solution.
You can set the data items of a ComboBox in the constructor:
ObservableList<String> data = FXCollections.observableArrayList("text1", "text2", "text3");
ComboBox<String> comboBox = new ComboBox<>(data);
or later:
comboBox.setItems(data);
To select a data item, you can select the appropriate index in the SelectionModel or the item itself:
comboBox.getSelectionModel().select(0);
comboBox.getSelectionModel().select("text1");
It's also possible to set a value to the combobox editor, which is not contained in the underlying datamodel:
comboBox.setValue("textXXX");
The "little text field" in a editable ComboBox is known as the editor of the ComboBox. And it's a normal TextField object. To access that object, you need to use the method ComboBox#getEditor(). This way you can use the methods of the TextField class. If I understand you correctly, all you want to do is set the text of that TextField.
This is done by doing comboBox.getEditor().setText(text) or comboBox.setValue(text). Both of these methods will set the text of the ComboBox.
But there's a difference when you want to fetch that text. ComboBox#getValue() ComboBox#getEditor()#getText() doesn't necessarily return the same value.
Consider the following example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestComboBox extends Application {
#Override
public void start(Stage stage) {
ComboBox<String> comboBox = new ComboBox<String>();
comboBox.setEditable(true);
comboBox.setValue("Test");
comboBox.getItems().addAll("Test", "Test2", "Test3");
VBox content = new VBox(5);
content.getChildren().add(comboBox);
content.setPadding(new Insets(10));
GridPane valueGrid = new GridPane();
Label cbValue = new Label();
cbValue.textProperty().bind(comboBox.valueProperty());
Label cbText = new Label();
cbText.textProperty().bind(comboBox.getEditor().textProperty());
valueGrid.add(new Label("ComboBox value: "), 0, 0);
valueGrid.add(new Label("ComboBox text: "), 0, 1);
valueGrid.add(cbValue, 1, 0);
valueGrid.add(cbText, 1, 1);
content.getChildren().add(valueGrid);
stage.setScene(new Scene(content));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
If you change the text in the ComboBox by chosing an alternative in the list, both ComboBox#valueProperty() and ComboBox#getEditor#textProperty() changes. But as you can see if you type something into the ComboBox, only the textProperty changes.
So use whichever method you want when you set the text of the ComboBox, but be aware of the difference when you want to retrieve that text.
Is there any way to add a changelistener to group of nodes for following changes?
For example, we can add a changelistener to a tabpane for getting tabselectedproperty.
I want to add changelistener a to a group of buttons for getting buttonActionedProperty! I want to get old button and new button....
Is there any way to do this?
When you compare the tabs in a tab pane to a collection of buttons, you're not really comparing like to like. A tab pane naturally has a sense of which tab is currently selected; buttons just generate events when they are pressed.
If you want your buttons to have a "selected" state, and want a collection of those grouped together so that only one is selected, then consider using ToggleButtons instead. You can put the toggle buttons into a ToggleGroup and register a listener with the toggle group's selectedToggle property:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ToggleButtonDemo extends Application {
#Override
public void start(Stage primaryStage) {
ToggleButton apples = new ToggleButton("Apples");
ToggleButton oranges = new ToggleButton("Oranges");
ToggleButton pears = new ToggleButton("Pears");
ToggleGroup fruitToggleGroup = new ToggleGroup();
fruitToggleGroup.getToggles().addAll(apples, oranges, pears);
fruitToggleGroup.selectedToggleProperty().addListener((obs, oldToggle, newToggle) ->
System.out.println("Selected toggle changed from "+oldToggle+" to "+newToggle));
HBox root = new HBox(5, apples, oranges, pears);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 350, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you really just want buttons, and don't have the notion of one of them being selected (I find it hard to see a use case for this), you can just create an ObjectProperty<Button> to store the last button on which an action occurred. Register an event listener with each button to update the property:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class LastActionTrackingDemo extends Application {
#Override
public void start(Stage primaryStage) {
Button apples = new Button("Apples");
Button oranges = new Button("Oranges");
Button pears = new Button("Pears");
ObjectProperty<Button> lastActionedButton = new SimpleObjectProperty<>();
EventHandler<ActionEvent> buttonActionHandler = event ->
lastActionedButton.set((Button) event.getSource());
apples.addEventHandler(ActionEvent.ACTION, buttonActionHandler);
oranges.addEventHandler(ActionEvent.ACTION, buttonActionHandler);
pears.addEventHandler(ActionEvent.ACTION, buttonActionHandler);
lastActionedButton.addListener((obs, oldButton, newButton) ->
System.out.println("Button changed from "+oldButton+" to "+newButton));
HBox root = new HBox(5, apples, oranges, pears);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 350, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Note there is a subtle different between the appearance of the two demos. The first (with toggle buttons) has a visual representation of which button is selected. The second does not. In both cases you can still set action listeners on the buttons if you need that functionality. There is also a (less subtle) difference in behavior: the toggle buttons can be "unselected"; so if you press the same toggle button twice, the selection goes back to null. This doesn't happen with the buttons.
This question already has answers here:
JavaFX accordion with multiple open panes
(3 answers)
Closed 8 years ago.
I've a Accordion with having multiple titledPane and there has a lots of data within titledPane's ListView panel. I just want to expand all the titledPane of Accordion when searching the data. I don't know how to do this. any idea?
Here, I've attached my real project's screen shots and it shows actually what I want to do.
Quick answer: You can't.
The Accordion has an expandedPane property, that is a single TitledPane. There is no way for an Accordion to have multiple expanded panes.
Instead, you can use multiple TitledPanes directly (inside a VBox or similar), to get the behavior you want. Unfortunately, this won't look just like an Accordion, because TitledPanes by default uses different styling. But with some custom CSS (look at caspian.css to see how accordions are styled) you could make it look just like the panes look in an Accordion.
With slightly more work, you could work this into your own "multiple selection accordion" control, for easier reuse.
Like harald said. You cant. But you can use multiple TitledPane's in another Container. VBox for instance. Try this code snippet.
import java.util.ArrayList;
import java.util.Collection;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Accordion;
import javafx.scene.control.TextArea;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TitledPanes extends Application {
public static void main(String [] args){ launch(args); }
#Override
public void start(Stage primaryStage) throws Exception {
HBox root = new HBox();
VBox noaccordion = new VBox();
noaccordion.getChildren().addAll(this.createPanes());
VBox yesaccordion = new VBox();
Accordion acc = new Accordion();
acc.getPanes().addAll(this.createPanes());
yesaccordion.getChildren().add(acc);
root.getChildren().addAll(noaccordion, yesaccordion);
primaryStage.setScene(new Scene(root,800,400));
primaryStage.show();
}
private Collection<TitledPane> createPanes(){
Collection<TitledPane> result = new ArrayList<TitledPane>();
TitledPane tp = new TitledPane();
tp.setText("Pane 1");
tp.setContent(new TextArea("Random text..."));
result.add(tp);
tp = new TitledPane();
tp.setText("Pane 2");
tp.setContent(new TextArea("Random text..."));
result.add(tp);
tp = new TitledPane();
tp.setText("Pane 3");
tp.setContent(new TextArea("Random text..."));
result.add(tp);
return result;
}
}