cbontoiu
cbontoiu

Reputation: 63

Text combo box factory call

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.

Upvotes: 1

Views: 194

Answers (2)

fabian
fabian

Reputation: 82461

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

Upvotes: 2

Hellomr
Hellomr

Reputation: 61

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:

enter image description here

I hope I understood you ritghtly and could help.

Upvotes: 0

Related Questions