I would like to know if there is any way to style a JavaFX ComboBox programmatically. I have tried to use the method setStyle(String); and styled the button, but it doesn't affect the list
Is there any way to do that?
you can change (for example) the text fill color of the cells of the list in the ComboBox like this:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group(), 200, 200);
ComboBox<String> myComboBox = new ComboBox<String>();
myComboBox.getItems().addAll("A", "B", "C", "D", "E");
myComboBox
.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> param) {
final ListCell<String> cell = new ListCell<String>() {
{
super.setPrefWidth(100);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
if (item.contains("A")) {
setTextFill(Color.RED);
} else if (item.contains("B")) {
setTextFill(Color.GREEN);
} else {
setTextFill(Color.BLACK);
}
} else {
setText(null);
}
}
};
return cell;
}
});
Group root = (Group) scene.getRoot();
root.getChildren().add(myComboBox);
stage.setScene(scene);
stage.show();
}
}
I think better way how to do it is set CSS ID or CSS class in code. For example your comboBox.
yourComboBox.setId("fancybox");
or set class:
yourComboBox.getStyleClass().clear();
yourComboBox.getStyleClass().add("fancyboxes");
and then style them in CSS.
then you can style almost everything on comboBox.
Example:
#fancyBox .cell {
-fx-text-fill: #4059a9;
}
there are many different "extensions" what you can add after #fancyBox and then style it. (Extension I mean that ".cell" after #fancyBox)
this can help you. Just keep searching.
Javafx combobox styling
Related
I want to ask if it is possible to make a chip in JFXChipView editable once it has been set.
You can create your own JFXChip and implement a behavior to enable editing. First, you need to have an editable label. I looked up online and I found this post: JavaFX custom control - editable label. Then, you can extend JFXChip to use that EditableLabel:
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXChip;
import com.jfoenix.controls.JFXChipView;
import com.jfoenix.svg.SVGGlyph;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.scene.layout.HBox;
public class EditableChip<T> extends JFXChip<Property<T>> {
protected final HBox root;
public EditableChip(JFXChipView<Property<T>> view, Property<T> item) {
super(view, item);
JFXButton closeButton = new JFXButton(null, new SVGGlyph());
closeButton.getStyleClass().add("close-button");
closeButton.setOnAction(event -> {
view.getChips().remove(item);
event.consume();
});
// Create the label with an initial value from the item
String initialValue = view.getConverter().toString(item);
EditableLabel label = new EditableLabel(initialValue);
label.setMaxWidth(100);
// Bind the item to the text in the label
item.bind(Bindings.createObjectBinding(() -> view.getConverter().fromString(label.getText()).getValue(), label.textProperty()));
root = new HBox(label, closeButton);
getChildren().setAll(root);
}
}
Note: I am using Property<T> instead of using the desired class T because JFXChipView stores the item the first time you add it. And in that case, you're going to get the values as you entered them the first time when calling JFXChipView#getChips().
Sample application:
import com.jfoenix.controls.JFXChipView;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class EditableChipViewApp extends Application {
#Override
public void start(Stage primaryStage) {
JFXChipView<Property<String>> chipView = new JFXChipView<>();
chipView.setChipFactory(EditableChip::new);
chipView.setConverter(new StringConverter<Property<String>>() {
#Override
public String toString(Property<String> object) {
return object == null ? null : object.getValue();
}
#Override
public Property<String> fromString(String string) {
return new SimpleStringProperty(string);
}
});
VBox container = new VBox(chipView);
Scene scene = new Scene(container, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Result:
This is how you get the actual values of the chips:
List<String> chipsValues = chipView.getChips().stream().map(Property::getValue).collect(Collectors.toList());
For my project in Java FX, I have a list of strings and I need to add them to a combo box with the requirement that only one of them (the first) be coloured in red.
I thought about encapsulating the strings in a Text and adding them to the combo box with appropriate setStyle("fx-text-fill: Color.xxx"). This calls for a setCellFactory() method and I don't know how to set up it correctly.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Mainu extends Application
{
final ObservableList<Text> SAMPUNITFRONT = FXCollections.observableArrayList(
new Text("complete"), new Text("seconds"), new Text("minutes"), new Text("hours"), new Text("days"));
#Override
public void start(Stage stage) throws Exception
{
ComboBox<Text> cb = new ComboBox<Text>();
for(int j = 0; j < SAMPUNITFRONT.size(); j++) // cycle over the list and generate a line with dashing defined by list
{
Text text = SAMPUNITFRONT.get(j);
if(text.getText().equals("complete"))
text.setStyle("-fx-text-fill: RED");
else
text.setStyle("-fx-text-fill: BLACK");
cb.getItems().add(text);
}
cb.setCellFactory(new Callback<ListView<Text>, ListCell<Text>>()
{
#Override public ListCell<Text> call(ListView<Text> p)
{
return new ListCell<Text>()
{
private final Text text;
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
text = new Text();
} // end Text
#Override protected void updateItem(Text item, boolean empty)
{
super.updateItem(item, empty);
if (item == null || empty)
{
setGraphic(null);
}
else
{
text.setStyle(item.getStyle());
setGraphic(text);
setItem(text);
}
} // end updateItem()
}; // end ListCell return
}
});
cb.getSelectionModel().selectFirst();
Pane root = new Pane();
root.getChildren().add(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {launch(args);}
}
At the moment, the drop down list of the combo is empty.
Several recommendations to make here:
Do not use a type of Node as item type. Instead store the data needed to determine the look of the ListCell in the items and let the ListCell implementations deal with the rest. You could simply use String as item type, if you the coloration should be done based on the text or index only or you could create a class containing 2 properties.
Do not call setItem yourself. Let the ComboBox/ListView deal with this.
For Text you need to use the -fx-fill css property instead of -fx-text-fill. The latter one would work, if you use the text property of the ListCell itself.
If you do not use the functionality of ObservableList, it's pointless to create one. You could simply have used List and Arrays.asList instead of ObservableList and FXCollections.observableArrayList for SAMPUNITFRONT
final ObservableList<String> SAMPUNITFRONT = FXCollections.observableArrayList("complete",
"seconds", "minutes", "hours", "days");
#Override
public void start(Stage stage) throws Exception {
ComboBox<String> cb = new ComboBox<String>(SAMPUNITFRONT);
cb.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> p) {
return new ListCell<String>() {
private final Text text;
{
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
text = new Text();
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
text.setStyle(item.equals("complete") ? "-fx-fill: red" : "-fx-fill: black");
text.setText(item);
setGraphic(text);
}
}
};
}
});
cb.getSelectionModel().selectFirst();
Pane root = new Pane();
root.getChildren().add(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
Alternatively without using a Text as graphic:
return new ListCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(item);
if (item != null) {
setStyle(item.equals("complete") ? "-fx-text-fill: red" : "-fx-text-fill: black");
}
}
};
The problem is that you set style to Text objects. Try set the same style to cells. And you don't need encapsulating the strings in a Text.
Here is your code with fixes:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Main extends Application
{
final ObservableList<String> SAMPUNITFRONT = FXCollections.observableArrayList("complete", "seconds", "minutes", "hours", "days");
#Override
public void start(Stage stage)
{
ComboBox<String> cb = new ComboBox<>();
cb.setCellFactory(cell -> new ListCell<String>()
{
#Override
protected void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
if (item == null || empty)
{
setGraphic(null);
setText("");
}
else
{
if (item.equals("complete"))
{
setStyle("-fx-text-fill: RED");
}
setText(item);
}
}
});
cb.getItems().addAll(SAMPUNITFRONT);
cb.getSelectionModel().selectFirst();
Pane root = new Pane();
root.getChildren().add(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
And result:
I hope I understood you ritghtly and could help.
So I am currently using my JFXListView and trying to set several checkboxes inside of it using CheckBoxListCell. Originally I used this:
listView.setCellFactory(CheckBoxListCell.forListView(new Callback<classForMenuOptions, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(UserMenuOptions item) {
return item.selectedProperty();
}
}));
Is there a way so that I can use JFXCheckBox instead of the traditional CheckBox?
You basically just need to implement your own cellFactory.
import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.controls.JFXListView;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ListViewExperiments extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
primaryStage.setTitle("ListView Experiment 1");
JFXListView<String> listView = new JFXListView<>();
listView.setPrefWidth(200);
listView.setCellFactory(lv -> new ListCell<String>()
{
JFXCheckBox checkBox = new JFXCheckBox();
#Override
public void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
if (empty) {
//setText(null);
setGraphic(null);
}
else {
checkBox.setText(item);
setGraphic(checkBox);
}
}
});
listView.getItems().add("Item 1");
listView.getItems().add("Item 2");
listView.getItems().add("Item 3");
HBox hbox = new HBox(listView);
Scene scene = new Scene(hbox, 300, 120);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
How can I make adding a value to items in a combo box possible so the user can either select from the existing items or clique "Add element" item to add a new item?
private ComboBox<String> comboStructDonnees;
Followed by:
comboData.getItems().addAll("TVW", "VWT", "TTVW", "VWXT", "Add item");
I don't know which event should I create next, I want to the text to be entered on the added element if possible.
Any help would be appreciated.
You can add an item with a "special value" (e.g. an empty string) to the end of the list of items for the combo box.
Use a cell factory to create a cell that displays a user-friendly message ("Add item..", for example) to the user when that value is displayed. Add an event filter to the cell that displays a dialog for inputting a new value if the cell is displaying the special value.
Here's a quick SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.TextInputDialog;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class AddItemToComboBox extends Application {
#Override
public void start(Stage primaryStage) {
ComboBox<String> combo = new ComboBox<>();
combo.getItems().addAll("One", "Two", "Three", "");
combo.setCellFactory(lv -> {
ListCell<String> cell = new ListCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
if (item.isEmpty()) {
setText("Add item...");
} else {
setText(item);
}
}
}
};
cell.addEventFilter(MouseEvent.MOUSE_PRESSED, evt -> {
if (cell.getItem().isEmpty() && ! cell.isEmpty()) {
TextInputDialog dialog = new TextInputDialog();
dialog.setContentText("Enter item");
dialog.showAndWait().ifPresent(text -> {
int index = combo.getItems().size()-1;
combo.getItems().add(index, text);
combo.getSelectionModel().select(index);
});
evt.consume();
}
});
return cell ;
});
BorderPane root = new BorderPane();
root.setTop(combo);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have a list with items which should carry RadioButton with list items.
the ListView is an observable ArrayList with data I want to add radio Button with each item in list View.
Create a custom ListCell and set the graphic of the ListCell to a RadioButton. You can add more functionality inside updateItem() if required.
Output
Complete Example
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class RadioButtonListView extends Application {
public static final ObservableList names =
FXCollections.observableArrayList();
private ToggleGroup group = new ToggleGroup();
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("List View Sample");
final ListView listView = new ListView();
listView.setPrefSize(200, 250);
listView.setEditable(true);
names.addAll(
"Adam", "Alex", "Alfred", "Albert",
"Brenda", "Connie", "Derek", "Donny",
"Lynne", "Myrtle", "Rose", "Rudolph",
"Tony", "Trudy", "Williams", "Zach"
);
listView.setItems(names);
listView.setCellFactory(param -> new RadioListCell());
StackPane root = new StackPane();
root.getChildren().add(listView);
primaryStage.setScene(new Scene(root, 200, 250));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private class RadioListCell extends ListCell<String> {
#Override
public void updateItem(String obj, boolean empty) {
super.updateItem(obj, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
RadioButton radioButton = new RadioButton(obj);
radioButton.setToggleGroup(group);
// Add Listeners if any
setGraphic(radioButton);
}
}
}
}