I have an issue with changing cell to an icon with the following code:
TableColumn typeCol = new TableColumn("Account Type");
typeCol.setCellFactory(new Callback<TableColumn<Account, String>, TableCell<Account, String>>() {
#Override
public TableCell<Account, String> call(TableColumn<Account, String> param) {
TableCell<Account,String> cell = new TableCell<Account, String>(){
#Override
public void updateItem(Account item, boolean empty){
if (item != null){
VBox vb = new VBox();
vb.setAlignment(Pos.CENTER);
ImageView imgVw = new ImageView(item.getTypeIcon());
imgVw.setFitHeight(10);
imgVw.setFitWidth(10);
vb.getChildren().addAll(imgVw);
setGraphic(vb);
}
}
};
return cell;
}
});
typeCol.setMinWidth(100);
typeCol.setCellValueFactory(
new PropertyValueFactory<Account, String>("type"));
The issue here is that for some reason I get en error of 'method does not override or implement a method form a supertype'. Any idaes?
TableCell<S, T> extends Cell<T>, not Cell<S>. Therefore the correct signature for the updateItem method of TableCell<Account, String> is
public void updateItem(String item, boolean empty)
Assuming your cellValueFactory
new PropertyValueFactory<Account, String>("type")
returns ObservableValues containing the URLs of images, you can use
ImageView imgVw = new ImageView(item);
instead of
ImageView imgVw = new ImageView(item.getTypeIcon());
Since the value passed to the updateItem method is the one that is contained in the ObservableValue returned by the cellValueFactory.
Sample code for placing an image in a table cell:
import javafx.scene.control.*;
import javafx.scene.image.*;
public class ImageTableCell<S> extends TableCell<S, Image> {
private final ImageView imageView = new ImageView();
public ImageTableCell() {
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(Image item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
imageView.setImage(null);
setText(null);
setGraphic(null);
}
imageView.setImage(item);
setGraphic(imageView);
}
}
This will work fine if your table doesn't represent millions of items. If you have many items and can't hold all of the potential images in memory, then you would need a TableCell instead of TableCell where the string is just the URL of the image rather than the actual image data itself. Then you would keep an LRU cache of image data which you would update in the updateItem, fetching the image data from the cache if it was there, otherwise loading it from the url. Such an approach could get a little tricky as you would probably want to be careful not to do too much dynamic loading of images as the user scrolls. In general, if you just have a few hundred or thousand thumbnail images, then the straight-forward approach defined in the code above would suffice rather than the alternate cache based approach.
Related
I would like to calculate a table's cell content asynchronously in the background. I came up with the following solution:
public abstract class AsynchronousCellFactory<E, T> implements Callback<TableColumn<E, T>, TableCell<E, T>> {
#Override
public TableCell<E, T> call(final TableColumn<E, T> param) {
final TableCell<E, T> cell = new TableCell<E, T>() {
private Service<T> service;
#Override
public void updateItem(final T item, final boolean empty) {
super.updateItem(item, empty);
if (service != null) {
service.cancel();
}
if (empty || this.getTableRow() == null || this.getTableRow().getItem() == null) {
setText(null);
} else {
setText("Calculating..");
final E rowDataItem = (E) this.getTableRow().getItem();
service = new Service<T>() {
#Override
protected Task<T> createTask() {
return getTask(rowDataItem);
}
};
service.setOnSucceeded(e -> {
setText(e.getSource().getValue().toString());
});
service.setOnFailed(e -> {
final Throwable t = e.getSource().getException();
setText(t.getLocalizedMessage());
});
service.start();
}
}
};
return cell;
}
protected abstract Task<T> getTask(E rowDataItem);
}
I use this factory for almost all of my columns to calculate different values.
But this does not work very well.
I frequently see cell contents saying Calculating. When I double click on that cell, which triggers a repaint apparently, the correct value appears.
Furthermore, even worse, I see sometimes that cell content is "switched" between columns: stringA, that should be in columnA, is in comlumnB, for example. Again, a double click on that cell fixes that.
I know that those factories are reused by the table columns to calculate cell content for different items, I assume the error is connected to that fact.
EDIT: When I move the service field to the TableCell, the Calculating error seems to be fixed, nevertheless, the switched-values problem persists.
Find a MWE here. It is actually not a working example, since it does not reproduce the problem. I guess the cell factory is working OK, and I am doing something wrong somewhere else.
I have problem with showing data using JavaFX lib.
Description of my problem:
I need to show lists of String's and every list have different count of elements.
All that i invented is create listview for every list.
But i have trouble with showing:
For example: I have listview with String's - and following simple code for it:
ListView<String> lst = new ListView<>();
ObservableList<String> observableList = FXCollections.observableArrayList("Hello", "Hello -2");
lst.setItems(observableList);
lst.setPrefHeight(100);
lst.setOrientation(Orientation.HORIZONTAL);
Scene scene = new Scene(new VBox(lst));
primaryStage.setScene(scene);
primaryStage.show();
And when i run it - i see something like:
But i want that elements in listview fills in space in view, i do not want this stripy space in right side.
In my task i have different lists with different count of elements.
If you can help me or get me idea - how it should be done - it will be great!
Thanx!
I think this is the solution that are you looking for:
public class TestController {
#FXML
private ListView<String> listView;
#FXML
public void initialize() {
ObservableList<String> observableList = FXCollections.observableArrayList("Hello", "Hello -2", "More", "Item");
listView.setItems(observableList);
listView.setPrefHeight(100);
Platform.runLater(() -> System.out.println(listView.getWidth()));
listView.setOrientation(Orientation.HORIZONTAL);
listView.setCellFactory(param -> new CustomListCell());
}
private class CustomListCell extends ListCell<String> {
CustomListCell() {
// If you want to use as a separate class you can use the getListView() instead of listView.
prefWidthProperty().bind(listView.widthProperty()
.divide(listView.getItems().size()) // set the width equally for each cell
.subtract(1)); // subtracted 1 to prevent displaying of a scrollBar, but you can play with
// this if you have many values in the listView
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
setText(item);
}
}
}
}
Hello I'm currently building project that uses javaFx and was wondering if it's possible to dynamically change the color of a listview objects cells at run-time. Each cell of the listview represents an object which has a color associated with it. The issue I'm having is that each object has a color that's generated at run time and the number of objects won't necessarily remain constant.
The underlying GUI that the javaFX code base is working with is fxml. I've tried looking into using CSS but since the amount of objects and color associated with those objects is generated at run-time I can't hard code it into a style-sheet. I've also been looking to using cell factories to create the desired cell for each cell, but I need to color to be passed into the cell factory and it has to be in the RGB format. Does anyone have any experience with a problem such as this one?
for(int i =0; i< mice.mice.size();i++){
selectedMiceListView.setCellFactory(new Callback<ListView<Object>,
ListCell<Object>>() {
// #Override
public ListCell<Object> call(final ListView<Object> param) {
final ListCell<Object> cell = new ListCell<Object>() {
#Override
protected void updateItem(final Object item, final boolean empty) {
super.updateItem(item, empty);
setStyle("-fx-background-color: rgb(" + mice.mice.get(i).red + "," + mice.mice.get(i).green + ", " + mice.mice.get(i).blue + ");");
}
};
return cell;
}
});
}
Below is what I've tried so far, it says that inner classes must effectively have final variables. Is there a way to modify it so that it accepts the rgb value i'm trying to set it too?
Try this, the below code paints every cell in the listview with black
this.listView
.setCellFactory(new Callback<ListView<Object>, ListCell<Object>>() {
#Override
public ListCell<Object> call(final ListView<Object> param) {
final ListCell<Object> cell = new ListCell<Object>() {
#Override
protected void updateItem(final Object item, final boolean empty) {
super.updateItem(item, empty);
setBackground(
new Background(new BackgroundFill(Color.rgb(100, 150, 200), CornerRadii.EMPTY, Insets.EMPTY)));
}
};
return cell;
}
});
I've created a simple TableView that is fed with data from a database, and what I want is just to be able to easily change the value of a numeric column of that table with JavaFx.
But... since I have some mental issue or something, I can't make it work.
Below it's the "SpinnerCell" component, and the issue I've been having is that even after the commitEdit is fired, when I get the items from the TableView, no values were altered. What am I missing from this update lifecycle?
import javafx.scene.control.Spinner;
import javafx.scene.control.TableCell;
public class SpinnerTableCell<S, T extends Number> extends TableCell<S, T> {
private final Spinner<T> spinner;
public SpinnerTableCell() {
this(1);
}
public SpinnerTableCell(int step) {
this.spinner = new Spinner<>(0, 100, step);
this.spinner.valueProperty().addListener((observable, oldValue, newValue) -> commitEdit(newValue));
}
#Override
protected void updateItem(T c, boolean empty) {
super.updateItem(c, empty);
if (empty || c == null) {
setText(null);
setGraphic(null);
return;
}
this.spinner.getValueFactory().setValue(c);
setGraphic(spinner);
}
}
Because your table cell is always showing the editing control (the Spinner), you bypass the usual table cell mechanism for beginning an edit. For example, in the TextFieldTableCell, if the cell is not in an editing state, then a label is shown. When the user double-clicks the cell, it enters an editing state: the cell's editingProperty() is set to true, and the enclosing TableView's editingCellProperty() is set to the position of the current cell, etc.
In your case, since this never happens, isEditing() is always false for the cell, and as a consequence, commitEdit() becomes a no-op.
Note that the CheckBoxTableCell is implemented similarly: its documentation highlights this fact. (The check box table cell implements its own direct update of properties via the selectedStateCallback.)
So there are two options here: one would be to enter an editing state when the spinner gains focus. You can do this by adding the following to the cell's constructor:
this.spinner.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (isNowFocused) {
getTableView().edit(getIndex(), getTableColumn());
}
});
Another option would be to provide a callback for "direct updates". So you could do something like:
public SpinnerTableCell(BiConsumer<S,T> update, int step) {
this.spinner = new Spinner<>(0, 100, step);
this.spinner.valueProperty().addListener((observable, oldValue, newValue) ->
update.accept(getTableView().getItems().get(getIndex()), newValue));
}
and then given a model class for the table, say
public class Item {
private int value ;
public int getValue() { return value ;}
public void setValue(int value) { this.value = value ;}
// ...
}
You could do
TableView<Item> table = ... ;
TableColumn<Item, Integer> valueCol = new TableColumn<>("Value");
valueCol.setCellValueFactory(cellData -> new SimpleIntegerProperty(cellData.getValue().getValue()).asObject());
valueCol.setCellFactory(tc -> new SpinnerTableCell<>(Item::setValue, 1));
In a tree-table-view there are child items that have in turn other child items. I need to customize, say, the text of certain cells of those pseudo-root items.
Is there a way to assign a css class/style to those items?
Update:
Ok, got it working with the following cellFactory:
treeTblColumnName.setCellFactory(new Callback<TreeTableColumn<FileModel, String>, TreeTableCell<FileModel, String>>() {
#Override
public TreeTableCell<FileModel, String> call(TreeTableColumn<FileModel, String> param) {
TreeTableCell<FileModel, String> cell = new TreeTableCell<FileModel, String>() {
#Override
protected void updateItem(String t, boolean bln) {
super.updateItem(t, bln); //To change body of generated methods, choose Tools | Templates.
System.out.println(this.getTableColumn().);
Label lbl = new Label(t);
lbl.setStyle("-fx-font-weight: bold; -fx-font-size: 14pt; -fx-text-fill: white;");
setGraphic(lbl);
}
};
return cell;
}
});
Now, how can I distinguish between classes? FileModel is an interface which is implemented by several classes. In this cellFactory I need to know what type is the current cell->item in order to apply different styling on it.
Thanks.
You can use instanceof:
FileModel fileModel = getTreeTableRow().getItem();
if(fileModel instanceof ExeFileModel){
//set style here
} else if(fileModel instanceof TxtFileModel){
//use another style here
}