Est-il possible de définir cell Factory deux fois pour Listview dans Javafx


J'ai défini une usine de cellules dans la vue Liste pour changer la couleur de la cellule sélectionnée.Et un autre est pour l'action glisser-déposer.Les deux ne fonctionnent pas en même temps. Usine de cellules: 1

listView.setCellFactory( new Callback<ListView<String>, ListCell<String>>()
        {
            @Override
            public ListCell<String> call( ListView<String> param )
            {
                ListCell<String> listCell = new ListCell<String>()
                {
                    @Override
                    protected void updateItem( String item, boolean empty )
                    {
                        super.updateItem( item, empty );
                        setText( item );
                    }
                };
Cell factory:2
listView.setCellFactory(list -> {

                    ListCell<String> cell = new ListCell<String>() {
                        @Override
                        protected void updateItem(String item, boolean empty) {
                            super.updateItem(item, empty);
                            setText(empty ? null : item);

                        }
                    };
Author: Pallavi, 2016-11-23

2 answers

setCellFactory est une méthode de jeu, et il se comporte comme n'importe quelle autre méthode: soit, il définit la valeur de la ListView's (seul et unique) cellFactory la propriété. Tout comme si vous aviez du code comme

someObject.setIntValue(5);
someObject.setIntValue(42);

Que Vous attendez someObject's intValue propriété 42, si vous appelez setCellFactory deux fois, la cellule de l'usine sera la deuxième valeur que vous lui transmettez.

L'approche la plus simple de ce que vous dites que vous essayez de faire est de simplement combiner toutes les fonctionnalités dans une seule usine de cellules:

listView.setCellFactory(list -> {

    ListCell<String> cell = new ListCell<String>() {
        @Override
        protected void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            setText(empty ? "" : item);

            // modify color here depending on item...
        }
    };

    cell.setOnDragDetected(e -> { /* ... */ });
    // other drag handlers...

    return cell ;
});

Si vous vous voulez vraiment séparer les choses en différentes implémentations d'usine, vous pouvez utiliser une approche de type "décorateur":

public class PlainCellFactory implements Callback<ListView<String, ListCell<String>> {
    @Override
    public ListCell<String> call(ListView<String> list) {
        return new ListCell<String>() {
            @Override
            protected void updateItem(String item, boolean empty) {
                super.updateItem(item, empty) ;
                setText(empty ? "" : item);
            }
        };
    }
}

Ajouter la couleur:

public class ColorListCellFactory implements Callback<ListView<String>, ListCell<String>> {

    private final Callback<ListView<String>, ListCell<String>> originalFactory ;

    public ColorListCellFactory(Callback<ListView<String>, ListCell<String>> originalFactory) {
        this.originalFactory = originalFactory ;
    }

    @Override
    public ListCell<String> call(ListView<String> list) {
        ListCell<String> cell = originalFactory.call(list);
        cell.itemProperty().addListener((obs, oldItem, newItem) -> {
            // change color depending on newItem (which might be null)...
        });
        return cell ;
    }
}

Et glisser-déposer:

public class DragAndDropListCellFactory implements Callback<ListView<String>, ListCell<String>> {

    private final Callback<ListView<String>, ListCell<String>> originalFactory ;

    public DragAndDropListCellFactory(Callback<ListView<String>, ListCell<String>> originalFactory) {
        this.originalFactory = originalFactory ;
    }

    @Override
    public ListCell<String> call(ListView<String> list) {
        ListCell<String> cell = originalFactory.call(list);
        cell.setOnDragDetected(e -> { /* ... */ });
        // other drag handlers...
        return cell ;
    }
}

Et maintenant vous pouvez faire

PlainCellFactory plainFactory = new PlainCellFactory();
ColorListCellFactory colorFactory = new ColorListCellFactory(plainFactory);
DragAndDropListCellFactory dndFactory = new DragAndDropListCellFactory(colorFactory);
listView.setCellFactory(dndFactory);
 3
Author: James_D, 2016-11-23 12:58:50

Est-il possible de définir cell factory deux fois pour ListView dans JavaFX?

Il est possible de remplacer la fabrique de cellules, mais vous ne pouvez pas faire en sorte que ListView utilise plus d'une fabrique pour créer une cellule.

Cependant, vous pouvez utiliser une usine de cellules qui gère l'initialisation/remplacement de l'élément en invoquant du code à partir d'autres objets (modèle de stratégie). Cela vous permet d'utiliser combiner différents comportements sans avoir besoin de réimplémenter la cellule.

public class MultiBehaviorListCell<S> extends ListCell<S> {

    public static interface Behavior<T> {

        default void initialize(MultiBehaviorListCell<T> cell) {
        }

        void updateItem(MultiBehaviorListCell<T> cell, T item, boolean empty);
    }

    public static <T> Callback<ListView<T>, ListCell<T>> factory(final Behavior<T>... behaviors) {
        Objects.requireNonNull(behaviors);
        return factory(Arrays.asList(behaviors));
    }

    private final Iterable<Behavior<S>> behaviors;

    private MultiBehaviorListCell(Iterable<Behavior<S>> behaviors) {
        this.behaviors = behaviors;

        // let behaviors handle initialisation
        for (Behavior b : behaviors) {
            try {
                b.initialize(this);
            } catch (RuntimeException ex) {
            }
        }
    }

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

        // let behaviors handle item replacement
        for (Behavior<S> b : behaviors) {
            try {
                b.updateItem(this, item, empty);
            } catch (RuntimeException ex) {
            }
        }
    }

    public static <T> Callback<ListView<T>, ListCell<T>> factory(final Iterable<Behavior<T>> behaviors) {
        Objects.requireNonNull(behaviors);
        return l -> new MultiBehaviorListCell<>(behaviors);
    }

}

Exemple utiliser

@Override
public void start(Stage primaryStage) {
    ListView<Integer> listView = new ListView<>();
    listView.getItems().addAll(1, 2, 3, 4);

    EventHandler<MouseEvent> mouseClicked = evt -> {
        MultiBehaviorListCell<Integer> source = (MultiBehaviorListCell<Integer>) evt.getSource();
        System.out.println(source.getItem());
    };

    listView.setCellFactory(MultiBehaviorListCell.factory(
            // setting text
            (cell, number, empty) -> cell.setText(empty || number == null ? "" : number.toString()),

            // changing background / text color
            new MultiBehaviorListCell.Behavior<Integer>() {

                @Override
                public void updateItem(MultiBehaviorListCell<Integer> cell, Integer item, boolean empty) {
                }

                @Override
                public void initialize(MultiBehaviorListCell<Integer> cell) {
                    cell.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
                    cell.setTextFill(Color.BLACK);
                }

            },

            // registering handler for non-empty cells
            (cell, number, empty) -> cell.setOnMouseClicked(empty ? null : mouseClicked)
    ));

    Scene scene = new Scene(listView);

    primaryStage.setScene(scene);
    primaryStage.show();
}
 1
Author: fabian, 2016-11-23 15:15:55