Reputation: 31
I have set one cell factory in List view to change the colour of selected Cell.And another one is for drag and drop action.Both are not working at the same time. Cell factory: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);
}
};
Upvotes: 3
Views: 1495
Reputation: 82461
Is it possible to set cell factory two times for
ListView
in JavaFX?
It's possible to replace the cell factory, but you cannot make ListView
use more than one factory to create a cell.
However you could use a cell factory that handles initialisation/replacement of the item by invoking code from other objects (strategy pattern). This allows you to use combine different behaviors without the need to reimplement the cell.
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);
}
}
Example use
@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();
}
Upvotes: 1
Reputation: 209340
setCellFactory
is a set method, and it behaves like any other set method: i.e. it sets the value of the ListView
's (one and only) cellFactory
property. Just like if you had code like
someObject.setIntValue(5);
someObject.setIntValue(42);
you would expect someObject
's intValue
property to be 42
, if you call setCellFactory
twice, the cell factory will be the second value you pass to it.
The simplest approach to what you say you are trying to do is to simply combine all the functionality in a single cell factory:
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 ;
});
If you really want to separate things out into different factory implementations, you can use a "decorator" type approach:
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);
}
};
}
}
add color:
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 ;
}
}
and drag and drop:
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 ;
}
}
And now you can do
PlainCellFactory plainFactory = new PlainCellFactory();
ColorListCellFactory colorFactory = new ColorListCellFactory(plainFactory);
DragAndDropListCellFactory dndFactory = new DragAndDropListCellFactory(colorFactory);
listView.setCellFactory(dndFactory);
Upvotes: 3