Issue
I'm trying to make a custom ListCell to my listview so i want the cell to be something like this :
so i want the listcell to be a Vbox which contain two hbox's :
the first hbox contain one label
the second hbox contain two labels
and then each time i iterate through my products that i get from database i add the name of the product and it's quantity and price to those labels and set those hbox's to the Container vbox then set all that to the ListCell.
this is what i did so far but can't make it work always i do that it redner empty Cell.
public void FetchingProducts() {
Product product = new Product();
Products = product.getProducts();
for (Product element:Products){
VBox vbox = new VBox();
HBox hbox1 = new HBox();
HBox hbox2 = new HBox();
Label label1 = new Label();
Label label2 = new Label();
Label label3 = new Label();
label1.setText(element.getNomProduit());
label2.setText(String.valueOf(element.getPrixVente()));
label3.setText(String.valueOf(element.getSeuilQte()));
hbox1.getChildren().add(label1);
hbox2.getChildren().addAll(label2,label3);
vbox.getChildren().addAll(hbox1,hbox2);
myList.setCellFactory((ListView<String> param) -> new ListCell<String>() {
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if ((empty || item == null)) {
setGraphic(null);
setText(null);
} else {
setGraphic(vbox);
}
}
});
}
}
After i run my code i get empty cells like this :

Has Anyone any idea how to make this work ?
Solution
Virutalized Controls
A ListView is a so-called "virtualized" control. This means it has a relatively small number of cells, at least enough to fill the viewport, that it reuses as needed. You may have thousands of items in the ListView, but only about 10 to 100 cells will exist at any given time.
Other virtualized controls include TableView, TreeTableView, and TreeView.
Custom Cells
You can customize the way a virtual control displays its items by creating a custom cell implementation. This is done by subclassing the appropriate cell class and overriding the updateItem(T,boolean) method. That method must be implemented correctly.
You must call
super.updateItem(item, empty)unconditionally (i.e., all code paths must call the super implementation at some point). Typically, this is the first statement in the method.If the cell is empty then you must update the cell to its "empty state". In most cases, that will simply mean setting the
textandgraphicproperties to null. Often a cell should also revert to its "empty state" when the item is null (yet the cell is not empty).Otherwise, you need to update the UI to match the new item (which may be null if the items list contains null elements).
It is the cell's responsibility to maintain its own visual state. It must update its UI appropriately when updateItem is called.
It is also important to realize that a Node can only appear once in the scene graph. If a node is already a child of some parent, then adding the node to a different parent will implicitly and quietly remove the node from its current parent. What this means a cell and a graphic have a one-to-one relationship. You cannot share a graphic between cells. And that means the cell should be responsible for creating its own graphic.
Note your code creates the graphic outside the cell. This would be "okay" if you did this for every cell, but you do not. Rather, you are creating the graphic per cell factory, which means the graphic is being shared with every cell created by that factory. Another thing that makes your approach incorrect is that you are setting the state of the graphic outside the cell. Given cells are reused, you must update the state of the graphic inside the updateItem method.
What may help here is to forget about the fact your ultimate goal is to display multiple items when implementing a custom cell. Think of the cell as designed to display a single item (or no item when empty) in isolation. Do not worry about how many items there are, what other cells are doing, or how the control is using the cells. Simply implement updateItem correctly and then have faith it will "just work".
Cell Factories
The way to customize which cell implementation is used with a virtualized control is by setting a cell factory. The factory is responsible for returning a new cell each time it is called. The factory is also associated with the entire control (or column for the table-like controls). It is not associated with each individual item. You should only set the cell factory once, when you are first initializing the control.
Note your code is looping over a "products" list and setting the cell factory each iteration. As just stated, you should only be setting the factory once. Setting the cell factory will replace the previous one. So, in addition to the graphic problem mentioned at the end of the Custom Cells section above, only the last iteration of your loop has any real effect.
Item Type
The whole point of custom cells and cell factories is to customize how an object is rendered in the UI. This means you should make the item type your actual model (or view-model) class. In other words, if you have a Product class that you want to display in a ListView, then you should ideally have a ListView<Product> instead of a ListView<String>. This will mean that updateItem is called with the appropriate Product argument, allowing you to update the cell's UI as needed.
It is a little different for tables, where you'd have a TableView<Product> but then have, for example, a TableColumn<Product, String> for the product's name column.
Example
Here is an example showing how to use a custom cell with ListView.
package com.example;
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
var productListView = new ListView<Product>();
productListView.setCellFactory(lv -> new ProductListCell());
productListView.getItems().addAll(createRandomProducts());
primaryStage.setScene(new Scene(productListView, 500, 800));
primaryStage.setTitle("Custom ListCell Example");
primaryStage.show();
}
private List<Product> createRandomProducts() {
var products = new ArrayList<Product>(100);
var random = new Random();
for (int i = 0; i < 100; i++) {
var name = "Product #" + (i + 1);
double price = random.nextDouble(5.0, 151.0);
int quantity = random.nextInt(1, 11);
products.add(new Product(name, price, quantity));
}
return products;
}
// Warning: Using floating-point numbers for money calculations would be
// wrong in a "real" application.
public record Product(String name, double price, int quantity) {
public double totalPrice() {
return price * quantity;
}
}
public static class ProductListCell extends ListCell<Product> {
private final GridPane grid = new GridPane();
private final Label nameLabel = new Label();
private final Label priceLabel = new Label();
private final Label quantityLabel = new Label();
public ProductListCell() {
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(10));
var colConstraints = new ColumnConstraints();
colConstraints.setMaxWidth(Double.MAX_VALUE);
colConstraints.setPercentWidth(50);
grid.getColumnConstraints().addAll(colConstraints, colConstraints);
grid.add(nameLabel, 0, 0, 2, 1);
grid.add(priceLabel, 0, 1);
grid.add(quantityLabel, 1, 1);
GridPane.setHalignment(quantityLabel, HPos.RIGHT);
}
@Override
protected void updateItem(Product item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setGraphic(grid);
nameLabel.setText(item.name());
priceLabel.setText(String.format("Total price: $%,.2f", item.totalPrice()));
quantityLabel.setText(String.format("Quantity: %,d", item.quantity()));
}
}
}
}
Answered By - Slaw

0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.