I try do create a customize item within ListView, let's say it's label and I want to execute setCellFactory, while I'm using Label I don't see the item (the text of the label), why?
ListView<Label> list = new ListView<Label>();
ObservableList<Label> data = FXCollections.observableArrayList(
new Label("123"), new Label("45678999"));
#Override
public void start(Stage stage) {
VBox box = new VBox();
Scene scene = new Scene(box, 200, 200);
stage.setScene(scene);
stage.setTitle("ListViewExample");
box.getChildren().addAll(list);
VBox.setVgrow(list, Priority.ALWAYS);
list.setItems(data);
list.setCellFactory(new Callback<ListView<Label>, ListCell<Label>>() {
#Override
public ListCell<Label> call(ListView<Label> list) {
ListCell<Label> cell = new ListCell<Label>() {
#Override
public void updateItem(Label item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setItem(item);
}
}
};
return cell;
}
}
);
stage.show();
}
If you do want to show items extending Node there is no need to use custom ListCells. The ListCells of the default factory are doing this already.
However in your case you're calling setItem instead of setGraphic and also you do not set the property back to null, when the cell becomes empty:
list.setCellFactory(new Callback<ListView<Label>, ListCell<Label>>() {
#Override
public ListCell<Label> call(ListView<Label> list) {
ListCell<Label> cell = new ListCell<Label>() {
#Override
public void updateItem(Label item, boolean empty) {
super.updateItem(item, empty);
// also sets to graphic to null when the cell becomes empty
setGraphic(item);
}
};
return cell;
}
});
Related
I have the following action when clicking on a cell of a Combobox in javafx.
Now i want the cell to be removed from the Combobox when it was clicked. How can I do that? Is there any way to remove a cell?
notificationCombo.setCellFactory(lv -> {
ListCell<String> cell = new ListCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : item);
}
};
cell.setOnMousePressed(e -> {
if (! cell.isEmpty()) {
for(Product product:products){
if(cell.getText().equals(product.getName())){
selectedLoadProduct = product;
loadProduct(selectedLoadProduct);
updateProd(selectedLoadProduct,false);
initGui();
selectedLoadProduct = null;
createProductSelection();
}}}
});
return cell ;
});
I would like to change background of a ComboBox based on selected item.
For example: if selected first item then background should be green, if second one is selected then red.
Is this possible?
If you want to just set the color of the ComboBox itself and not the items of the ComboBox inside the drop-down list, you can create a custom binding between the buttonCellProperty and the valueProperty of the ComboBox to implement the custom coloring.
Example
This example colors the ComboBox to green if the first item is selected, to red if the second item is selected, and leaves the coloring as it is otherwise.
Update: The background color of the arrow button of the ComboBox is also colored now, for that the lookup method can be used to get the arrow button: StackPane arrowButton = (StackPane) combo.lookup(".arrow-button");.
ComboBox<String> combo = new ComboBox<>();
combo.setItems(FXCollections.observableArrayList("First", "Second", "Third", "Fourth"));
combo.buttonCellProperty().bind(Bindings.createObjectBinding(() -> {
int indexOf = combo.getItems().indexOf(combo.getValue());
Color color = Color.TRANSPARENT;
switch (indexOf) {
case 0: color = Color.GREEN; break;
case 1: color = Color.RED; break;
default: break;
}
final Color finalColor = color;
// Get the arrow button of the combo-box
StackPane arrowButton = (StackPane) combo.lookup(".arrow-button");
return new ListCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setBackground(Background.EMPTY);
setText("");
} else {
setBackground(new Background(new BackgroundFill(finalColor, CornerRadii.EMPTY, Insets.EMPTY)));
setText(item);
}
// Set the background of the arrow also
if (arrowButton != null)
arrowButton.setBackground(getBackground());
}
};
}, combo.valueProperty()));
The result is like:
Notes
1) If you want to color the items also in the drop-down list, you can pick the solution from other answers here.
2) If you do not display Strings but items that are able to store also the color of the item, the solution is even shorter, just use the color of the selected item in updateItem method rather than calculating your own color.
Sure this is possible. Create custom ListCells in a cellFactory you use with the ComboBox and use it to modify the Cell's style based on the item it contains.
Example:
public class Item {
public Item(String value, Color color) {
this.value = value;
this.color = color;
}
private final String value;
private final Color color;
public String getValue() {
return value;
}
public Color getColor() {
return color;
}
}
ComboBox<Item> comboBox = new ComboBox<>(FXCollections.observableArrayList(
new Item("Summer", Color.RED),
new Item("Winter", Color.CYAN),
new Item("Spring", Color.LIME),
new Item("Autumn", Color.BROWN)
));
comboBox.setCellFactory(lv -> new ListCell<Item>(){
#Override
protected void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setBackground(Background.EMPTY);
setText("");
} else {
setBackground(new Background(new BackgroundFill(item.getColor(),
CornerRadii.EMPTY,
Insets.EMPTY)));
setText(item.getValue());
}
}
});
comboBox.setButtonCell(comboBox.getCellFactory().call(null));
public class Main extends Application {
Random r = new Random();
private class Item {
Color c;
String s;
public Item(Color a,String b){
c = a;
s = b;
}
#Override
public String toString() {
// TODO Auto-generated method stub
return s;
}
}
private void addAll(List<String> items){
for(String s : items){
box.getItems().add(new Item(Color.WHITE,s));
}
}
ComboBox<Item> box;
#Override
public void start(Stage primaryStage) {
Pane p;
try {
p = new StackPane();
box = new ComboBox<Item>();
box.setMinWidth(100);
addAll(Font.getFontNames());
box.setCellFactory(new Callback<ListView<Item>, ListCell<Item>>() {
#Override
public ListCell<Item> call(ListView<Item> param) {
// TODO Auto-generated method stub
return new ListCell<Item>(){
#Override
public void updateSelected(boolean selected) {
super.updateSelected(selected);
if(selected){
getItem().c = Color.rgb(r.nextInt(205),
r.nextInt(205), r.nextInt(205), 1);
}
setStyle("-fx-text-fill: black; -fx-background-color: #" +
getItem().c.toString().substring(2)+";");
}
#Override
protected void updateItem(Item item, boolean empty) {
// TODO Auto-generated method stub
super.updateItem(item, empty);
if(empty){
return;
}
setText(item.toString());
setStyle("-fx-text-fill: black; -fx-background-color: #" +
getItem().c.toString().substring(2)+";");
}
};
}
});
p.getChildren().add(box);
p.setPrefSize(500, 500);
Scene scene = new Scene(p);
scene.getStylesheets().add(getClass().
getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
combobox.valueProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue ov, String t, String t1) {
if (t1.equals("Option 1")) {
combobox.setStyle(" -fx-background-color: #000000");
}
if (t1.equals("Option 2")) {
combobox.setStyle(" -fx-background-color: #FFFFFF");
}
}
});
you should be able to do something quite simple like the above with a basic change listener, you may have to refresh the page (I often just remove and re-add the component so you can see the change take place)
This is on the basis you want to select a combobox item then the box to change, not each item to have a different colour from the start
I'm currently working on javafx and I have a serious problem. I can't find a way to get the index of the row of a button that I created dynamically on a table view.
If someone could help me out, that would be very helpful.
this.clmColumn.setCellFactory((TableColumn<?, ?> column) -> {
return new TableCell<?, ?>() {
#Override
protected void updateItem(? item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
final HBox hbox = new HBox(5);
final VBox vbox = new VBox(5);
Label label = new Label(item.toString());
final Button btnMais = new Button("+");
btnMais.setMinSize(25, 25);
final TableCell<?, ?> c = this;
btnMais.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// At this point i want to select the current ROW of the button that i pressed on the tableview.
}
});
final Button btnMenos = new Button("-");
btnMenos.setMinSize(25, 25);
btnMenos.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if (getItem() > 1) {
// At this point i want to select the current ROW of the button that i pressed on the tableview.
}
}
});
final Button btnRemover = new Button("Remover");
btnRemover.setFont(new Font(8));
btnRemover.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// At this point i want to select the current ROW of the button that i pressed on the tableview.
}
});
vbox.getChildren().add(hbox);
vbox.getChildren().add(btnRemover);
hbox.getChildren().add(btnMais);
hbox.getChildren().add(label);
hbox.getChildren().add(btnMenos);
hbox.setAlignment(Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
setGraphic(vbox);
} else {
setGraphic(null);
}
}
};
});
In the handle() method you can do
Object row = getTableView.getItems().get(getIndex());
You can replace Object with a more specific type if you use more specific types throughout in the type parameters.
I'am working on a java project using javafx multiple input types.but i am having a strangle ComboBox behaviours since i use Labels with images(ImageView) on it.
1- Combobox looks in white! but i need it in black.
2- and every time i choose an item.
3- it disappear!!!
Here is my code:
...
import javafx.scene.control.ComboBox;
import javafx.scene.image.ImageView;
ImageView img_tun = new ImageView("images/icones/flag/Tunisia.png");
Label lbl_tun=new Label("1",img_tun);
ImageView img_fr = new ImageView("images/icones/flag/France.png");
Label lbl_fr=new Label("2",img_fr);
ImageView img_aut = new ImageView("images/icones/flag/World.png");
Label lbl_aut=new Label("3",img_aut);
optionsnat=FXCollections.observableArrayList(lbl_tun,lbl_fr,lbl_aut);
#FXML
ComboBox<Label> cb_nat = new ComboBox<Label>();
private String nat="1";
...
#Override
public void initialize(URL location, ResourceBundle resources) {
...
cb_nat.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number number, Number number2) {
if(cb_nb.getItems().get((Integer) number2)=="1"){setNat("1");}
else if(cb_nb.getItems().get((Integer) number2)=="2"){setNat("2");}
else if(cb_nb.getItems().get((Integer) number2)=="3"){setNat("3");}
else{System.err.println("Erreur lors de changement de nation..");}
}
});
}
...
and code.fxml
<ComboBox fx:id="cb_nat" layoutX="40.0" layoutY="265.0" prefWidth="150.0" />
EDIT:
After reading this Article i know that my approach is tottaly wrong and strongly not recommended.. if anyone have another ideas to put bnation flags in ComboBox please help!!
thanks..(Sorry for my bad english)
What is causing this problem is that when you choose a ListCell, its item (Label in our situation) is being moved by the ComboBox from the ListCell (Items observableList) to the ButtonCell, the ButtonCell is the small box that is empty by default. However, we all know that any Node object cannot be placed twice anywhere inside the same scene, and since there is no clone function for the ListCell class, javafx removes it from its last place to the new place which is the ButtonCell.
The solution is to add strings
items in the list and provide a cell factory to create the label node inside the cell factory. Create a class called "StringImageCell" and do the following:
You need to set the cellFactory property:
cb_nat.setCellFactory(listview -> new StringImageCell());
You need to set the buttonCell property: cb_nat.setButtonCell(new StringImageCell());
Here is an example:
public class ComboBoxCellFactory extends Application {
#Override
public void start(Stage stage) throws Exception {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll("1", "2", "3");
//Set the cellFactory property
comboBox.setCellFactory(listview -> new StringImageCell());
// Set the buttonCell property
comboBox.setButtonCell(new StringImageCell());
BorderPane root = new BorderPane();
root.setCenter(comboBox);
Scene scene = new Scene(root, 600, 600);
stage.setScene(scene);
stage.show();
}
//A Custom ListCell that displays an image and string
static class StringImageCell extends ListCell<String> {
Label label;
static HashMap<String, Image> pictures = new HashMap<>();
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setItem(null);
setGraphic(null);
} else {
setText(item);
ImageView image = getImageView(item);
label = new Label("",image);
setGraphic(label);
}
}
}
private static ImageView getImageView(String imageName) {
ImageView imageView = null;
switch (imageName) {
case "1":
case "2":
case "3":
if (!pictures.containsKey(imageName)) {
pictures.put(imageName, new Image(imageName + ".png"));
}
imageView = new ImageView(pictures.get(imageName));
break;
default:
imageName = null;
}
return imageView;
}
public static void main(String[] args) {
launch(args);
}
}
I put the numbers in my textfields located in Mirror column, But when I handle the scroll bar (scroll my table), the contents of the textfields are deleted. Is there any solution to this problem?
The code that creates my table:
#SuppressWarnings({ "unchecked", "rawtypes" })
private void loadTabMirror() {
TableColumn request = new TableColumn("Pedido");
request.setCellValueFactory(new PropertyValueFactory<MirrorVo, String>("request"));
request.setMinWidth(150);
TableColumn applicant = new TableColumn("Requerente");
applicant.setCellValueFactory(new PropertyValueFactory<MirrorVo, String>("name"));
applicant.setMinWidth(400);
TableColumn numberRg = new TableColumn("N° RG");
numberRg.setCellValueFactory(new PropertyValueFactory<MirrorVo, String>("rg"));
numberRg.setMinWidth(80);
Callback<TableColumn<MirrorVo, String>, TableCell<MirrorVo, String>> txtMirrorCallBack =
new Callback<TableColumn<MirrorVo, String>, TableCell<MirrorVo, String>>() {
#Override
public TableCell call(final TableColumn param) {
final TableCell cell = new TableCell() {
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else if(item == null){
TextField textField = loadRuleTextField(param,getIndex());
textFieldMirrors.add(textField);
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
};
return cell;
}
};
TableColumn mirror = new TableColumn("Espelho");
mirror.setCellValueFactory(new PropertyValueFactory<MirrorVo, Integer>("mirror"));
mirror.setMinWidth(100);
mirror.setCellFactory(txtMirrorCallBack);
Callback<TableColumn<MirrorVo, String>, TableCell<MirrorVo, String>> imgMirrorInfo =
new Callback<TableColumn<MirrorVo, String>, TableCell<MirrorVo, String>>() {
#Override
public TableCell<MirrorVo, String> call(
TableColumn<MirrorVo, String> param) {
final TableCell cell = new TableCell() {
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
ImageView imgViewInfo = new ImageView(new Image("resources/img/info.png"));
imgViewInfo.setFitWidth(32);
imgViewInfo.setFitHeight(32);
imgViewInfo.setVisible(false);
imgViewInfos.put(getIndex(), imgViewInfo);
setGraphic(imgViewInfo);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
};
return cell;
}
};
TableColumn info = new TableColumn("Status");
info.setMinWidth(40);
info.setCellFactory(imgMirrorInfo);
Callback<TableColumn<CivilRecord, String>, TableCell<CivilRecord, String>> btnInvalidateCallBack = addActionBtnInvalidate();
TableColumn validation = new TableColumn(" ");
validation.setCellValueFactory(new PropertyValueFactory<CivilRecord, String>("dateRegisterLot"));
validation.setMinWidth(100);
validation.setCellFactory(btnInvalidateCallBack);
Callback<TableColumn<CivilRecord, String>, TableCell<CivilRecord, String>> btnAutoGenerateCallBack = addActionBtnAutoGenerate();
TableColumn autoGenerate = new TableColumn(" ");
autoGenerate.setCellValueFactory(new PropertyValueFactory<CivilRecord, String>("dateRegisterLot"));
autoGenerate.setMinWidth(100);
autoGenerate.setCellFactory(btnAutoGenerateCallBack);
tabMirror.getColumns().addAll(request,applicant,numberRg,mirror,info,autoGenerate, validation);
}
Your TableCell for the mirror column never actually handles the case where the item has content (i.e. the item is not null).
As you scroll, what probably happens is that TableCells which are scrolled off the visible portion of the screen get updated as empty cells, then they get reused at some point for new items that are displayed. So the updateItem() method is first called with empty=true (setting the text and graphic to null), and is later called with empty=false and item as the item to be displayed. Since your updateItem method doesn't handle this case at all, the cell's text and graphic remain null.
You need to change the updateItem method so that it handles all possible cases:
empty == true (you already have)
empty == false && item == null (you already have)
empty == false && item != null (you do not handle)