Поле со списком с индикаторами выполнения не работает должным образом

У меня есть этот пример выпадающего списка с индикаторами выполнения.

package javafxapplication4;

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;


public class JavaFXApplication4 extends Application
{

    @Override
    public void start(Stage primaryStage)
    {

     double y1 = 15;
        ProgressBar p1 = new ProgressBar();
        p1.setLayoutY(y1);
    VBox vb1 = new VBox();
    vb1.getChildren().addAll(new Label("Progressbar 1"), p1);

    double y2 = 15;
        ProgressBar p2 = new ProgressBar();
        p2.setLayoutY(y2);
    VBox vb2 = new VBox();
    vb2.getChildren().addAll(new Label("Progressbar 2"), p2);

    double y3 = 15;
        ProgressBar p3 = new ProgressBar();
        p3.setLayoutY(y3);
    VBox vb3 = new VBox();
    vb3.getChildren().addAll(new Label("Progressbar 3"), p3);


    TextChooser textChooser = new TextChooser(
        vb1, vb2, vb3
    );

    textChooser.setStyle("-fx-font: 10px \"Verdana\";");





        StackPane root = new StackPane();
        root.getChildren().add(textChooser);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    public static class TextChooser extends StackPane {
    private Label label = new Label();

    private ComboBox<VBox> combo = new ComboBox<>();

    public TextChooser(VBox... options) {
        StackPane.setAlignment(label, Pos.CENTER_LEFT);
        StackPane.setAlignment(combo, Pos.CENTER_LEFT);

        label.graphicProperty().bind(
            //combo.getSelectionModel().selectedItemProperty()
            combo.getSelectionModel().selectedItemProperty()
        );
        label.visibleProperty().bind(
            combo.visibleProperty().not()
        );
        //label.setPadding(new Insets(0, 0, 0, 10));

        combo.getItems().setAll(options);

        combo.setCellFactory(new Callback<ListView<VBox>, ListCell<VBox>>() {
            @Override public ListCell<VBox> call(ListView<VBox> p) {
                return new ListCell<VBox>() {
                    @Override protected void updateItem(VBox item, boolean empty) {
                        super.updateItem(item, empty);

                        if (item == null || empty) {
                            setGraphic(null);
                        } else {
                            setGraphic(item);
                        }
                    }
                };
            }
        });

        combo.getSelectionModel().select(0);
        combo.setVisible(true);

        label.setOnMouseEntered(event -> combo.setVisible(true));
        combo.showingProperty().addListener(observable -> {
            if (!combo.isShowing()) {
                combo.setVisible(false);
            }
        });
        combo.setOnMouseExited(event -> {
            if (!combo.isShowing()) {
                combo.setVisible(false);
            }
        });

        getChildren().setAll(label, combo);
    }

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

Я хочу отображать индикатор выполнения с рабочими фоновыми процессами, когда приложение работает. Когда я навожу мышь на индикатор выполнения, я хочу видеть все процессы. Но по какой-то причине код не работает должным образом - когда я выбираю индикатор выполнения, индикатор выполнения не отображается. Можете ли вы помочь мне исправить этот код.


person Peter Penzov    schedule 11.12.2013    source источник


Ответы (2)


Как опубликовано здесь https://stackoverflow.com/a/20630806/3110608

public class JavaFXApplication5 extends Application
{
    public static void main( String[] args )
    {
        launch( args );
    }


    public class ProgressData
    {
        private final DoubleProperty  progressProp = new SimpleDoubleProperty();
        private final StringProperty  progressName = new SimpleStringProperty();

        public ProgressData( String name, double progress )
        {
            progressProp.set( progress );
            progressName.set( name );
        }

        public DoubleProperty progressProperty()
        {
            return  progressProp;
        }

        public StringProperty nameProperty()
        {
            return  progressName;
        }

        @Override
        //  Lazy hack for the combo button.
        public String toString()
        {
            return progressName.get();
        }
    }


    @Override
    public void start( Stage primaryStage )
    {
        ProgressData vb1 = new ProgressData( "Progressbar 1", -1 );
        ProgressData vb2 = new ProgressData( "Progressbar 2", 0.2 );
        ProgressData vb3 = new ProgressData( "Progressbar 3", 0.3 );

        TextChooser textChooser = new TextChooser( vb1, vb2, vb3 );

        textChooser.setStyle( "-fx-font: 10px \"Verdana\";" );

        StackPane root = new StackPane();
        root.getChildren().add( textChooser );

        Scene scene = new Scene( root, 300, 250 );

        primaryStage.setTitle( "Hello World!" );
        primaryStage.setScene( scene );
        primaryStage.show();
    }

    public static class TextChooser extends StackPane
    {
        private final Label label = new Label();

        private final ComboBox<ProgressData> combo = new ComboBox<>();

        public TextChooser(ProgressData... options)
        {
            StackPane.setAlignment( label, Pos.CENTER_LEFT );
            StackPane.setAlignment( combo, Pos.CENTER_LEFT );


            final ProgressBar  labelBar = new ProgressBar();
            label.visibleProperty().bind( combo.visibleProperty().not() );
            label.setContentDisplay( ContentDisplay.RIGHT );
            label.setGraphic( labelBar );


            combo.getItems().setAll( options );

            //  This will change the label's text and the progress bar value.
            combo.getSelectionModel().selectedItemProperty().addListener( new ChangeListener<ProgressData>()
            {
                @Override
                public void changed( ObservableValue<? extends ProgressData> observable, ProgressData oldValue, ProgressData newValue )
                {
                    if ( labelBar.progressProperty().isBound() )
                    {
                        labelBar.progressProperty().unbind();
                    }

                    labelBar.progressProperty().bind( newValue.progressProperty() );
                    label.setText( newValue.nameProperty().get() );
                }
            } );

            combo.setCellFactory( new Callback<ListView<ProgressData>, ListCell<ProgressData>>()
            {
                @Override
                public ListCell<ProgressData> call( ListView<ProgressData> p )
                {
                    return new ListCell<ProgressData>()
                    {
                        private final ProgressBar  cellBar = new ProgressBar();
                        {
                            cellBar.setMouseTransparent( true );
                            setContentDisplay( ContentDisplay.RIGHT );
                            setGraphic( cellBar );
                        }

                        @Override
                        protected void updateItem( ProgressData item, boolean empty )
                        {
                            super.updateItem( item, empty );

                            if ( item != null && ! empty )
                            {
                                if ( cellBar.progressProperty().isBound() )
                                {
                                    cellBar.progressProperty().unbind();
                                }
                                cellBar.progressProperty().bind( item.progressProperty() );
                                setText( item.nameProperty().get() );
                            }
                        }
                    };
                }
            } );

            combo.getSelectionModel().select( 0 );
            combo.setVisible( true );

            label.setOnMouseEntered( new EventHandler<MouseEvent>()
            {
                @Override
                public void handle( MouseEvent event )
                {
                    combo.setVisible( true );
                }
            } );

            combo.showingProperty().addListener( new InvalidationListener()
            {
                @Override
                public void invalidated( Observable observable )
                {
                    if ( !combo.isShowing() )
                    {
                        combo.setVisible( false );
                    }
                }
            } );

            combo.setOnMouseExited( new EventHandler<MouseEvent>()
            {
                @Override
                public void handle( MouseEvent event )
                {
                    if ( !combo.isShowing() )
                    {
                        combo.setVisible( false );
                    }
                }
            } );

            getChildren().setAll( label, combo );
        }

    }

}
person Jurgen    schedule 18.12.2013

Для изменения отображения в "кнопке" (т.е. отображения для выбранного значения) необходимо использовать

combo.setButtonCell(...);

Однако я не думаю, что это сработает в вашем случае. Проблема в том, что у вас есть подкласс Node (VBox) в качестве типа вашего ComboBox. Следовательно, выбранный элемент появится в двух местах графа сцены: один раз в выпадающем списке и один раз в кнопке ComboBox. Как правило, использовать Nodes в качестве типа поля со списком — очень плохая идея: см. Javadocs:

Помещать узлы в список элементов настоятельно не рекомендуется.

Вместо этого вы должны использовать тип данных для ComboBox и создать VBox в реализациях ListCell.

person James_D    schedule 11.12.2013
comment
Не могли бы вы показать мне, как исправить этот код. Это очень продвинуто для меня. - person Peter Penzov; 11.12.2013
comment
Просто посмотрите пример в Javadocs. Также есть пример отображения хода выполнения фоновых задач по адресу gist.github.com/james-d/7722752. В этом примере используется ListView вместо ComboBox, но основная идея аналогична. - person James_D; 11.12.2013
comment
Я не могу решить эту проблему. Можете ли вы отредактировать мою правку и исправить проблему? - person Peter Penzov; 14.12.2013
comment
Что вы подразумеваете под типом данных? В вашем примере setCellFactory. Можете ли вы выделить часть вашего кода, которую я должен использовать? - person Peter Penzov; 16.12.2013
comment
Под типом данных я подразумеваю класс, инкапсулирующий данные, которые вы представляете (а не класс, инкапсулирующий то, как вы представляете данные). Здесь вы говорите, что показываете фоновые процессы, поэтому тип должен быть каким-то классом, инкапсулирующим фоновый процесс. - person James_D; 16.12.2013
comment
@James_D звучит интересно и эффективно, что вы сказали в своем ответе. Можете ли вы показать нам более эффективный пример реализации ProgressBars в ComboBox?? - person DeepSidhu1313; 18.07.2015