How to put images in ListView javafx - java

In my ListView myList, I want each item(String) to have a mini photo next to it.
Here is my how my ListView myList is defined:
ListView<String> myList = new ListView<String>();
SearchResultList.setCellFactory(new Callback<ListView<String>,ListCell<String>>() {
#Override
public ListCell<String> call(ListView<String> list) {
return new ColorRectCell();
}
}
);
I read you must specify a cell factory which updates each item in list. However I don't know how this all works, This is the code where I specify my cell factory
static class ColorRectCell extends ListCell<String> {
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
Image rect = new Image("huisteken.jpg");
ImageView rec = new ImageView(rect);
if (item != null) {
System.out.println("testing" + item +"######");
setGraphic(rec);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
}
Please, any ideas or tips are welcome.

My solution to accomplish this is to set the Cells text to null and to make Graphic contain a Hbox containing both picture and text. So make your updateItem look like this:
#Override
void updateItem(final String item, final boolean empty) {
super.updateItem(item, empty);
// if null, display nothing
if (empty || item == null) {
setText(null);
setGraphic(null);
return;
}
setText(null);
Label textLabel = new Label(item + " ");
final HBox hbox = new HBox();
hbox.setSpacing(5);
Label iconLabel = new Label();
iconLabel.setGraphic(new ImageView(new Image("huisteken.jpg")));
hbox.getChildren().addAll(iconLabel, textLabel);
setGraphic(hbox);
}
`

Related

JavaFX: How wrap text in tablecell without a listener?

Currently I use a listener to wrap text on a tablecell but that causes a little delay when load/reload data table. Are there any other way to do this without use a listener?
TableColumn<Peticion, Void> colObs = new TableColumn<>("Observaciones");
colObs.setPrefWidth(200);
colObs.getStyleClass().add("columData");
colObs.setCellFactory(col->{
TableCell<Peticion, Void> cell = new TableCell<Peticion, Void>(){
public void updateItem(Void item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
Peticion p = getTableView().getItems().get(getIndex());
Label l = new Label(p.getObservaciones());
l.setWrapText(true);
VBox box = new VBox(l);
l.heightProperty().addListener((observable,oldValue,newValue)-> {
box.setPrefHeight(newValue.doubleValue()+7);
Platform.runLater(()->this.getTableRow().requestLayout());
});
super.setGraphic(box);
}
}
};
return cell;
});

Initialize table column based on button click

I am fairly new in JavaFX. I have a table with multiple columns and two buttons (btnBuilding , btnBSearch) outside the table. In the table, I have a column colAction where I want to have some buttons based on the button clicked outside the table. Suppose if I click btnBuilding I want to have 2 button Save and Refresh in my colAction column and Whenever I click btnBSearch I want to have 2 button Edit and Add in my colAction column. Inside the initialize() I tried like below
colAction.setCellFactory(col -> {
Button SaveButton = new Button("Save");
Button AddButton = new Button("Add");
Button RefreshButton = new Button("Refresh");
Button EditButton = new Button("Edit");
HBox hbox = new HBox(5);
if(btnBSearch.isFocused())
hbox.getChildren().addAll(AddButton,EditButton);
else if(btnBuilding.isFocused())
hbox.getChildren().addAll(SaveButton,RefreshButton);
TableCell<ModelBrBuilding, ModelBrBuilding> cell = new TableCell<ModelBrBuilding, ModelBrBuilding>() {
#Override
//Updating with the number of row
public void updateItem(ModelBrBuilding building, boolean empty) {
super.updateItem(building, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(hbox);
}
}
};
EditButton.setOnAction((ActionEvent event)->{
});
RefreshButton.setOnAction(event->{
});
SaveButton.setOnAction((ActionEvent event) -> {
});
AddButton.setOnAction(event -> {
});
return cell ;
});
But the problem is whatever button I click I am always getting Add and Edit in my action column. How can I add different button in my column based on the button (resides outside the table) I click?
The cellFactory runs only once for each cell. You need to make sure the cell is updated the button outside of the table is clicked.
You could do this by creating a property that contains a factory for the graphics and listen to it.
public interface GraphicFactory<T> {
Node createGraphic();
void updateGraphic(Node graphic, T item);
}
public class ReplacableGraphicTableCell<S, T> extends TableCell<S, T> {
private final ChangeListener<GraphicFactory<T>> factoryListener = (o, oldValue, newValue) -> {
if (newValue == null || isEmpty()) {
setGraphic(null);
} else {
Node n = newValue.createGraphic();
newValue.updateGraphic(n, getItem());
setGraphic(n);
}
};
private final ObservableValue<GraphicFactory<T>> factory;
private ReplacableGraphicTableCell(ObservableValue<GraphicFactory<T>> factory) {
this.factory = factory;
factory.addListener(factoryListener);
}
public static <E, F> Callback<TableColumn<E, F>, TableCell<E, F>> forTableColumn(ObservableValue<GraphicFactory<F>> factory) {
if (factory == null) {
throw new IllegalArgumentException();
}
return column -> new ReplacableGraphicTableCell(factory);
}
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
GraphicFactory<T> fact = factory.getValue();
if (fact == null) {
setGraphic(null);
} else {
Node graphic = getGraphic();
if (graphic == null) {
graphic = fact.createGraphic();
setGraphic(graphic);
}
fact.updateGraphic(graphic, item);
}
}
}
}
final ObjectProperty<GraphicFactory<Item>> graphicFactory = new SimpleObjectProperty<>();
TableColumn<Item, Item> column = new TableColumn<>();
column.setCellValueFactory(cd -> new SimpleObjectProperty<>(cd.getValue()));
column.setCellFactory(ReplacableGraphicTableCell.forTableColumn(graphicFactory));
ToggleGroup tg = new ToggleGroup();
tg.selectedToggleProperty().addListener((o, oldValue, newValue) -> {
GraphicFactory<Item> factory = null;
if (newValue != null) {
factory = (GraphicFactory<Item>) newValue.getUserData();
}
graphicFactory.set(factory);
});
RadioButton rb = new RadioButton("Add/Edit");
rb.setUserData(new GraphicFactory<Item>() {
#Override
public Node createGraphic() {
Button add = new Button("Add");
Button edit = new Button("Edit");
HBox hbox = new HBox(add, edit);
add.setOnAction(evt -> {
System.out.println("Add " + hbox.getUserData());
});
edit.setOnAction(evt -> {
System.out.println("Edit " + hbox.getUserData());
});
return hbox;
}
#Override
public void updateGraphic(Node graphic, Item item) {
graphic.setUserData(item);
}
});
rb.setToggleGroup(tg);
RadioButton rb2 = new RadioButton("Save/Refresh");
rb2.setUserData(new GraphicFactory<Item>() {
#Override
public Node createGraphic() {
Button save = new Button("Save");
Button refresh = new Button("Refresh");
HBox hbox = new HBox(save, refresh);
save.setOnAction(evt -> {
System.out.println("Save " + hbox.getUserData());
});
refresh.setOnAction(evt -> {
System.out.println("Refresh " + hbox.getUserData());
});
return hbox;
}
#Override
public void updateGraphic(Node graphic, Item item) {
graphic.setUserData(item);
}
});
rb2.setToggleGroup(tg);
It will not work this way. To begin with, you need to process the btnBuilding and btnBSearch buttons. Which of the buttons is pressed must reflect in the table you are using. For this purpose, one feature can be created propert to reflect which of the two buttons is pressed.
BooleanProperty showSearch = new SimpleBooleanProperty(false);
...
btnBuilding.setOnAction(e -> showSearch.setValue(false));
btnBSearch.setOnAction(e -> showSearch.setValue(true));
Then, you link the colAction column to the value of the property.
colAction.setCellValueFactory(cdf -> showSearch);
In this situation, you can create CellFactory to create the dynamic content cell
colAction.setCellFactory(col -> {
return new TableCell<String, Boolean>() {
Button SaveButton = new Button("Save");
Button AddButton = new Button("Add");
Button RefreshButton = new Button("Refresh");
Button EditButton = new Button("Edit");
HBox hboxBuilding = new HBox(5);
HBox hboxSearch = new HBox(5);
{
hboxBuilding.getChildren().addAll(AddButton,EditButton);
hboxSearch.getChildren().addAll(SaveButton,RefreshButton);
}
#Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
}
else {
setGraphic(item ? hboxBuilding : hboxSearch);
}
}
};
});

How can I to force JavaFX ComboBox dropdown list cells to be re-rendered?

I have a Java 8 editable ComboBox where the default item is bolded and the non-default items are unbolded by a CellFactory.
When the default is changed to another item, the only way I have found to get the new default bolded and the old default unbolded by the CellFactory is to delete and re-add both the old and new default items. This necessitates a fair bit of code to deal with a new item becoming current because the previous current item was removed and then setting the current item to the item we were modifying.
Is there a nice way to flag a cell as dirty and needing re-rendering so I don't have to remove and re-add 2 cells?
I have spent half a day googling but maybe I'm not asking the right questions.
I suppose the controlling code would be simpler if I just cleared all the combobox items and re-added them all, but that's not very efficient.
Edit: As requested, here is CellFactory:
bookComboBox.setCellFactory(
new Callback<ListView<String>, ListCell<String>>() {
#Override public ListCell<String> call(ListView<String> param) {
final ListCell<String> cell = new ListCell<String>() {
{
super.setPrefWidth(100);
}
#Override public void updateItem(String item,
boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item);
if (item.equals(Book.getDefaultBook())) {
setFont(Font.font("System", FontWeight.BOLD, 14));
System.out.println("bookComboBox.setCellFactory: set BOLD item=" + item);
}
else {
setFont(Font.font("System", FontWeight.NORMAL, 14));
System.out.println("bookComboBox.setCellFactory: set NORMAL item=" + item);
}
}
else {
setText(null);
}
}
};
return cell;
}
});
I'm not changing the values of the cells, just the font used when rendering.
I'm not using onEditCommit().
I use bookComboBox.setOnAction listener to react to combobox selection changes.
The setOnAction event also occurs when text is entered in the combobox and Action key (ENTER) is pressed or Combobox loses focus.
By using a ObservableValue<Boolean> that tells you whether the item should be bold or not for each cell, you can create a binding that updates the font.
Example:
This uses each item as default for 1 sec before using the next as default.
#Override
public void start(Stage primaryStage) {
ComboBox<String> comboBox = new ComboBox<>(FXCollections.observableArrayList(
"item 1",
"item 2",
"item 3",
"item 4",
"item 5"
));
final Font BOLD_FONT = Font.font("System", FontWeight.BOLD, 14);
final Font NORMAL_FONT = Font.font("System", FontWeight.NORMAL, 14);
final StringProperty defaultValue = new SimpleStringProperty();
comboBox.setCellFactory(lv -> new ListCell<String>() {
{
// use bold font if the item property contains the
// same value as the defaultValue property
fontProperty().bind(Bindings.when(itemProperty().isEqualTo(defaultValue))
.then(BOLD_FONT)
.otherwise(NORMAL_FONT));
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
setText(item);
}
}
});
// change default every second
PauseTransition animation = new PauseTransition(Duration.seconds(1));
EventHandler<ActionEvent> eventHandler = new EventHandler<ActionEvent>() {
private Iterator<String> iterator = comboBox.getItems().iterator();
#Override
public void handle(ActionEvent event) {
if (!iterator.hasNext()) {
iterator = comboBox.getItems().iterator();
}
defaultValue.set(iterator.next());
animation.play();
}
};
animation.setOnFinished(eventHandler);
eventHandler.handle(null);
Scene scene = new Scene(comboBox);
primaryStage.setScene(scene);
primaryStage.show();
}

Multiple scene nodes in PropertySheet Editor JavaFX

I want to add checkbox and textfield to one property of PropertySheet (ControlsFX library). Is it possible or no? So, i just need to add some GUI elements together to one PropertyEditor, for example checkbox + button, checkbox + label, checkbox + textfield and etc. Is it possible to override PropertyEditor to do it?
you could also wrap multiples nodes inside a single parent .[See Here
Solved by myself. I tried to add checkbox + combobox to HBox. Code below, it works.
public static final <T> PropertyEditor<?> createCheckBoxLinkEditor(PropertySheet.Item property,
final Collection<T> choices) {
ComboBox<T> comboBox = new ComboBox<T>();
comboBox.setCellFactory((ListView<T> p) -> new ListCell<T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
} else if (item instanceof Class) {
setText(((Class) item).getSimpleName());
} else {
setText(item.toString());
}
}
});
HBox hbox = new HBox(5);
CheckBox checkBox = new CheckBox();
hbox.getChildren().add(checkBox);
hbox.getChildren().add(comboBox);
//hbox.getA
//comboBox.setConverter(value);
return new AbstractPropertyEditor<T, HBox>(property, hbox) {
{
comboBox.setItems(FXCollections.observableArrayList(choices));
//new AutoCompleteComboBoxListener(comboBox);
new SelectKeyComboBoxListener(comboBox);
}
#Override
protected ObservableValue<T> getObservableValue() {
return comboBox.getSelectionModel().selectedItemProperty();
}
#Override
public void setValue(T value) {
comboBox.getSelectionModel().select(value);
}
};
}

JavaFX TreeTableView - center displayed values inside column

I need to center the values displayed inside columns of a treetableview, how Can i change the position from left to center?
final TreeTableColumn<RootMaster, Integer> dataColumn = new TreeTableColumn<>("Data");
dataColumn.setEditable(false);
dataColumn.setMinWidth(300);
dataColumn.setCellValueFactory(new TreeItemPropertyValueFactory<RootMaster, Integer>("bu..."));
You need to set a cellFactory on the TreeTableColumn (as well as the cellValueFactory).
dataColumn.setCellFactory(col -> {
TreeTableCell<RootMaster, Integer> cell = new TreeTableCell<RootMaster, Integer>() {
#Override
public void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
setText(item.toString());
}
}
};
cell.setAlignment(Pos.CENTER);
return cell ;
});

Categories