Text combo box factory call - java

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.

Related

Is there a CheckBoxListCell equivalent for JFoenix so that we can use JFXCheckBox instead of the traditional one?

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);
}
}

Styling JavaFX ComboBox without CSS file

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

Adding a value to Combo Box from the UI?

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);
}
}

Set custom value in ComboBox

I have ComboBox which I would like to use to configure Service delay period:
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class MainApp extends Application
{
public static void main(String[] args)
{
launch(args);
}
private MyService myService = new MyService();
#Override
public void start(Stage stage)
{
myService.setDelay(new Duration(300));
myService.setPeriod(new Duration(1000));
myService.start();
stage.setTitle("ComboBoxSample");
Scene scene = new Scene(new Group(), 450, 250);
ComboBox emailComboBox = new ComboBox();
emailComboBox.getItems().addAll("Stop", "1 Second", "5 Seconds", "10 Seconds", "15 Seconds");
emailComboBox.setPromptText("Email address");
emailComboBox.valueProperty().addListener(new ChangeListener<String>()
{
#Override
public void changed(ObservableValue ov, String t, String t1)
{
if (t1.equals("Stop"))
{
myService.cancel();
}
if (t1.equals("1 Second"))
{
myService.setPeriod(new Duration(1000));
}
if (t1.equals("5 Second"))
{
myService.setPeriod(new Duration(5000));
}
if (t1.equals("10 Second"))
{
myService.setPeriod(new Duration(10000));
}
if (t1.equals("15 Second"))
{
myService.setPeriod(new Duration(15000));
}
}
});
GridPane grid = new GridPane();
grid.setVgap(4);
grid.setHgap(10);
grid.setPadding(new Insets(5, 5, 5, 5));
grid.add(new Label("To: "), 0, 0);
grid.add(emailComboBox, 1, 0);
Group root = (Group) scene.getRoot();
root.getChildren().add(grid);
stage.setScene(scene);
stage.show();
}
}
Is there any tricky way to reduce too many if() switch cases which I use to set Service delay period.
I want to add emailComboBox.setEditable(true); and based on my custom input I want to set the service Delay period.
Since the user is effectively choosing a Duration, the data type of the ComboBox should be Duration, instead of String. Install a cell factory to configure how the Duration objects are displayed in the combo box:
ComboBox<Duration> combo = new ComboBox<>(
FXCollections.observableArrayList(
Duration.UNKNOWN,
Duration.seconds(1),
Duration.seconds(5),
Duration.seconds(10),
Duration.seconds(15)));
combo.setCellFactory(lv -> createListCell());
combo.setButtonCell(createListCell());
combo.valueProperty().addListener((obs, oldValue, newValue) -> {
if (newValue == null || newValue == Duration.UNKNOWN) {
myService.cancel();
} else {
myService.setPeriod(newValue);
}
});
With the custom cell implementation looking something like
private ListCell<Duration> createListCell() {
return new ListCell<Duration>() {
#Override
public void updateItem(Duration item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
if (item == Duration.UNKNOWN) {
setText("Stop");
} else {
setText(String.format("%.0f Second", item.toSeconds()));
}
}
}
};
}

Replace a selected Label with a TextField

I have created ListView of Labels using :
ListView<Label> list = new ListView<Label>();
Image folder = new Image(getClass().getResourceAsStream("folder.png"));
ObservableList<Label> data = FXCollections.observableArrayList();
for (int i = 0; i < 6; i++) {
Label lbl = new Label();
lbl.setText("label" + i);
lbl.setGraphic(new ImageView(folder));
lbl.setContentDisplay(ContentDisplay.LEFT);
lbl.setGraphicTextGap(10.2);
data.add(lbl);
}
list.setItems(data);
I want the user to be able to double click on any of the Labels within the ListView, the selected Label should be replaced with a TextField so that the user can enter a new label name dynamically.
After the user presses Enter the TextField should turn back into a Label.
Don't use Label as the type of data for the ListView. Use String. Then you can just use the standard TextFieldListCell which has exactly the functionality you describe. Since you want a graphic in the standard cell display, just subclass TextFieldListCell and override the appropriate methods to include the graphic when the text field is not displayed:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
public class EditableListViewTest extends Application {
#Override
public void start(Stage primaryStage) {
ListView<String> list = new ListView<>();
Image testImg = new Rectangle(12, 12, Color.CORNFLOWERBLUE).snapshot(null, null);
for (int i = 0; i < 6; i++) {
list.getItems().add("label "+i);
}
StringConverter<String> identityStringConverter = new DefaultStringConverter();
list.setCellFactory(lv -> new TextFieldListCell<String>(identityStringConverter) {
private ImageView imageView = new ImageView(testImg);
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (! empty && ! isEditing()) {
setStaticGraphic();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setStaticGraphic();
}
#Override
public void commitEdit(String newValue) {
super.commitEdit(newValue);
setStaticGraphic();
}
private void setStaticGraphic() {
setGraphic(imageView);
setContentDisplay(ContentDisplay.LEFT);
setGraphicTextGap(10.2);
}
});
list.setEditable(true);
primaryStage.setScene(new Scene(new BorderPane(list), 250, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Categories