Home > Enterprise >  Add a button at the end of a ComboBox list view
Add a button at the end of a ComboBox list view

Time:10-06

I need to create a combo box which can have a button at the end of its list view. This list can have items added or removed and displays a scroll bar when the number of items is over 5. Also, this list view does not close itself when an item is selected.

How to add a button "New item" like in the following screen shots ?

enter image description here

Here is the source code:

helloApplication.java

package com.example.demo;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

HelloController.java

package com.example.demo;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.skin.ComboBoxListViewSkin;

import java.net.URL;
import java.util.ResourceBundle;

public class HelloController implements Initializable {

    @FXML
    private ComboBox<String> frequencyPlanComboBox;

    @Override
    public void initialize(URL pURL, ResourceBundle pResourceBundle) {
        frequencyPlanComboBox.getItems()
                             .addAll("aaa", "bbb", "ccc");
        ComboBoxListViewSkin<String> comboBoxListViewSkin = new ComboBoxListViewSkin<String>(frequencyPlanComboBox);
        comboBoxListViewSkin.setHideOnClick(false);
        frequencyPlanComboBox.setSkin(comboBoxListViewSkin);
    }
}

hello-view.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>

<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demo.HelloController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
    </padding>

    <Label fx:id="welcomeText" />
   <ComboBox fx:id="frequencyPlanComboBox" prefWidth="150.0" visibleRowCount="5" />
</VBox>

CodePudding user response:

Edited version:

As pointed out in the comments, the mock-up in the OP has the button outside the list view, so that it is visible without scrolling to the bottom of the list view. You can accomplish this by creating a custom skin which overrides the getPopupContent() method:

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Bounds;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.skin.ComboBoxListViewSkin;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;

import java.net.URL;
import java.util.ResourceBundle;

public class HelloController implements Initializable {

    @FXML
    private ComboBox<String> frequencyPlanComboBox;

    @Override
    public void initialize(URL pURL, ResourceBundle pResourceBundle) {

        for (int i = 1 ; i <=20 ;i  ) {
            frequencyPlanComboBox.getItems().add("Item " i);
        }

        ComboBoxListViewSkin<String> comboBoxListViewSkin = new ComboBoxListViewSkin<String>(frequencyPlanComboBox) {
            private Button button = new Button("Add New");

            private VBox pane = new VBox();
            {
                pane.setStyle("-fx-background-color: -fx-control-inner-background;");
            }

            @Override
            public Node getPopupContent() {
                Node defaultContent = super.getPopupContent();
                defaultContent.setManaged(true);
                pane.getChildren().setAll(defaultContent, button);
                return pane ;
            }
        };
        comboBoxListViewSkin.setHideOnClick(false);
        frequencyPlanComboBox.setSkin(comboBoxListViewSkin);

    }
}

Here is the original solution, which is probably less desirable:

Add a null value to the end of the combo box items list, and use a custom list cell which displays the button for that value.

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.skin.ComboBoxListViewSkin;

import java.net.URL;
import java.util.ResourceBundle;

public class HelloController implements Initializable {

    @FXML
    private ComboBox<String> frequencyPlanComboBox;

    @Override
    public void initialize(URL pURL, ResourceBundle pResourceBundle) {
        frequencyPlanComboBox.getItems()
                .addAll("aaa", "bbb", "ccc", null);
        ComboBoxListViewSkin<String> comboBoxListViewSkin = new ComboBoxListViewSkin<String>(frequencyPlanComboBox);
        comboBoxListViewSkin.setHideOnClick(false);
        frequencyPlanComboBox.setSkin(comboBoxListViewSkin);

        frequencyPlanComboBox.setCellFactory(lv -> new ListCell<>() {
            private Button button = new Button("Add New...");
            {
                button.setOnAction(e -> {
                    /*
                    Do something
                     */
                    System.out.println("Button pressed");
                });
            }
            @Override
            protected void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setText("") ;
                    setGraphic(null);
                } else if (item == null) {
                    setText("");
                    setGraphic(button);
                } else {
                    setText(item);
                    setGraphic(null);
                }
            }
        });
    }
}

You probably want to experiment a bit with styling that cell, so it doesn't appear selected.

  • Related