How do i set the grapic for a TextFieldTreeCell - javafx

I made a treeview with normal treecells and a cellfactory to display my custom objects.
I wanned to be able to edit the object names in the treeview.
So i switched from a normal TreeCell to a TextFieldTreeCell so that i can edit the name of my objects in the tree.
But now the setGrapic methods in updateitem is not working. what am i doing wrong?
Also im not sure how to acces the custom object from the fromString methode for editing the object.
treeView.setCellFactory(new Callback<TreeView<MenuItem>, TreeCell<MenuItem>>() {
#Override
public TreeCell<MenuItem> call(TreeView<MenuItem> menuItemTreeView) {
// MenuItemTreeCell menuItemTreeCell = new MenuItemTreeCell();
TextFieldTreeCell<MenuItem> cell = new TextFieldTreeCell<MenuItem>(new StringConverter<MenuItem>() {
#Override
public String toString(MenuItem menuItem) {
if(menuItem==null){
return "";
}
return menuItem.getName();
}
#Override
public MenuItem fromString(String s) {
// todo add code for changing name
return null;
}
})
{
#Override
public void updateItem(MenuItem item, boolean empty) { // empty was b
super.updateItem(item, empty);
// setText((empty || item == null) ? "" : item.getName());
// checking if not null , and then what class to determ icon
if (item != null) {
switch (item.getClass().getSimpleName()) {
case "Group":
setGraphic(new ImageView(caseicon));
break;
case "Item":
setGraphic(new ImageView(basicicon));
break;
case "ClassItem":
setGraphic(new ImageView(classicon));
break;
case "MechanicItem":
setGraphic(new ImageView(mechanicicon));
break;
}
} else {
// no icon
setGraphic(null);
}
super.updateItem(item, empty);
}
};

Related

How can i color a specific row with specific condition in TableView?

I am using jfxml to create my tableview and use the Controller to action it .
in the Controller i want to make a specific row with a different color with specific action for my Cell it is TextFieldTableCell .
And this some code from action :
coldisc.setCellFactory(TextFieldTableCell.forTableColumn());
coldisc.setOnEditCommit(e -> {
e.getTableView().getItems().get(e.getTablePosition().getRow()).setDisc(e.getNewValue());
Brand b = tablebrand.getSelectionModel().getSelectedItem();
TablePosition pos2 = tablebrand.getFocusModel().getFocusedCell();
Calc();
CalcDisc();
//**********
Platform.runLater(() -> tablebrand.edit(pos2.getRow(), colafterdisc));
//**********
if (pos2.getRow() == tablebrand.getItems().size() - 1) {
tablebrand.getItems().add(new Brand("", "", "", "", "", "", ++ts));
}
Platform.runLater(() -> tablebrand.edit(pos2.getRow() + 1, colcode));
tablebrand.getSelectionModel().clearAndSelect(pos2.getRow() + 1, colcode);
final TablePosition pos3 = tablebrand.getFocusModel().getFocusedCell();
CalcTot();
}
});
One way of doing this is by conditionally adding a style class to the rows where needed:
final String styleClass = "color-class";
tableView.setRowFactory(new Callback<TableView<Foo>, TableRow<Foo>>() {
#Override
public TableRow<Foo> call(TableView<Foo> fooTableView) {
return new TableRow<Foo>() {
#Override
protected void updateItem(Foo foo, boolean empty) {
super.updateItem(foo, empty);
if (/* condition */) {
getStyleClass().add(styleClass);
} else { /* remove if condition no longer true */
getStyleClass().remove(styleClass);
}
}
};
}
});
Then in your css file, you can add the required color:
.table-row-cell.color-class {
-fx-background-color: /*your color*/;
}
This works using setStyle method.
If a specific condition is matched, then add : setStyle("your custom styling");
If not, just add setStyle("");
Let's figure out how it works, just imagine you want to highlight rows with null values :
// styling javafx tablerow with null values -> background color : red
Callback<TableView<T>, TableRow<T>> rowFactory = new Callback<TableView<T>, TableRow<T>>() {
#Override
public TableRow<T> call(TableView<T> l) {
return new TableRow<T>() {
#Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
try {
// isNull() is a custom method to catch null values in a row
if (isNull()) {
setStyle("-fx-background-color: #ff8080;");
} else {
// No empty cells - No specific highlights
setStyle("");
}
} catch (NullPointerException e) {
}
}
};
}
};
tv.setRowFactory(rowFactory);

How to force TableRow repaint

How to force TableRow repaint ?.
Imagine the following scenario: The tableView is updated every 180 milliseconds, but the cell that receives the TableRow style information is not visible, and every time it is upgraded TableRow needs to be repainted. When I use refresh() method, it does not look good, especially with the mouse positioning on the TableView, it blinks and in this case consuming cpu.
myTableView.setRowFactory( new Callback<TableView, TableRow<Line>>() {
#Override
public TableRow call(final TableView p) {
return new TableRow<Line>() {
#Override
public void updateItem(Line item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
if(item.statusProperty().getValue().equals("BORDER")) {
setStyle("-fx-border-color:green;-fx-border-width:2;-fx-opacity:1;");
}
}
}
};
}
});
Since the style depends on the statusProperty() of the Line, which is observable, you can use a binding:
#Override
public void updateItem(Line item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
styleProperty().bind(Bindings
.when(item.statusProperty().isEqualTo("BORDER"))
.then("-fx-border-color:green;-fx-border-width:2;-fx-opacity:1;")
.otherwise(""));
} else {
styleProperty().unbind();
setStyle("");
}
}
An alternative way to create the binding, which is probably more convenient if the logic is more complicated, is
#Override
public void updateItem(Line item, boolean empty) {
super.updateItem(item, empty);
if(item != null) {
styleProperty().bind(Bindings.createStringBinding(() -> {
if ("BORDER".equals(item.getStyle())) {
return "-fx-border-color:green;-fx-border-width:2;-fx-opacity:1;" ;
} else {
return "" ;
}
}, item.statusProperty());
} else {
styleProperty().unbind();
setStyle("");
}
}
This way the table row will observe the current item's status property, and automatically update the style if that property changes.
If you really want to make the code cleaner, of course, you should move the styles to an external CSS file. You can create a CSS PseudoClass (or more than one) which you can set and unset on the row:
final PseudoClass borderPC = PseudoClass.getPseudoClass("border");
myTableView.setRowFactory(p -> {
TableRow<Line> row = new TableRow<>();
ChangeListener<String> statusListener = (obs, oldStatus, newStatus) ->
row.pseudoClassStateChanged(borderPC, "BORDER".equals(newStatus)) ;
row.itemProperty().addListener((obs, oldLine, newLine) -> {
if (oldLine != null) {
oldLine.statusProperty().removeListener(statusListener);
}
if (newLine == null) {
row.pseudoClassStateChanged(borderPC, false);
} else {
newLine.statusProperty().addListener(statusListener);
row.pseudoClassStateChanged(borderPC, "BORDER".equals(newLine.getStatus()));
}
};
return row ;
});
Then in your external CSS file, do
.table-row-cell:border {
-fx-border-color:green;
-fx-border-width:2;
-fx-opacity:1;
}
Again, you can easily add more psuedoclasses, more rules to the CSS, and additional tests and pseudoclass updates using this approach.

JavaFX row in table view didn't remove ater remove element

I have a litre problem with table view. When I remove element from observable list, the row correspandont on this element is not deleted.
I have a relations list ( person1, type, person2 => children)
When I add a new child the rellation is created (null, neutral, null => child).
After add some people, I change the relations beetwen them, so, when I indicate the siblings, some realtions are deleted. But it is still visible in table view. It is not selectable, but when I click on, it is indique . the last relation in the list.
When I add new Person, the row are overrided.
this.relationSimLeftColumn.setCellValueFactory(cellData -> cellData.getValue().simLeftProperty());
this.relationSimRightColumn.setCellValueFactory(cellData -> cellData.getValue().simRightProperty());
this.relationTypeColumn.setCellValueFactory(cellData -> cellData.getValue().typeProperty());
and cell factory :
private Callback<TableColumn<GTX_Relation, GTX_Member>,
TableCell<GTX_Relation, GTX_Member>> setMemberCellFactory(String parameter) {
Callback<TableColumn<GTX_Relation, GTX_Member>, TableCell<GTX_Relation, GTX_Member>> callback =
new Callback<TableColumn<GTX_Relation, GTX_Member>, TableCell<GTX_Relation, GTX_Member>>() {
#Override
public TableCell<GTX_Relation, GTX_Member> call(TableColumn<GTX_Relation, GTX_Member> param) {
TableCell<GTX_Relation, GTX_Member> cell = new TableCell<GTX_Relation, GTX_Member>() {
#Override
protected void updateItem(GTX_Member item, boolean empty) {
super.updateItem(item, empty);
ImageView imageview = new ImageView();
if (item != null) {
imageview.setFitHeight(TABLE_IMAGE_MEMBER_HEIGHT);
imageview.setFitWidth(TABLE_IMAGE_MEMBER_WIDTH);
imageview.setImage(new Image(item.getPhoto()));
setGraphic(imageview);
} else {
if (!empty) {
imageview.setFitHeight(TABLE_IMAGE_MEMBER_HEIGHT);
imageview.setFitWidth(TABLE_IMAGE_MEMBER_WIDTH);
String path = parameter.equals("LEFT") == true ?
ImageFiles.NO_NAME_FEMALE.toString() : ImageFiles.NO_NAME_MALE.toString();
imageview.setImage(new Image(path));
setGraphic(imageview);
}
}
}
};
return cell;
}
};
return callback;
}
Your cell factory needs to handle the case where the cell is empty.
When you remove an item from the table's items list, a cell that was previously used to display an item will (potentially) be reused as an empty cell; i.e. its updateItem(null, true) method will be called. Your current implementation doesn't do anything for the case where item == null && empty == true, so the cell's appearance won't be changed.
You need
#Override
protected void updateItem(GTX_Member item, boolean empty) {
super.updateItem(item, empty);
ImageView imageview = new ImageView();
if (item != null) {
imageview.setFitHeight(TABLE_IMAGE_MEMBER_HEIGHT);
imageview.setFitWidth(TABLE_IMAGE_MEMBER_WIDTH);
imageview.setImage(new Image(item.getPhoto()));
setGraphic(imageview);
} else {
if (empty) {
setGraphic(null);
} else {
imageview.setFitHeight(TABLE_IMAGE_MEMBER_HEIGHT);
imageview.setFitWidth(TABLE_IMAGE_MEMBER_WIDTH);
String path = parameter.equals("LEFT") == true ?
ImageFiles.NO_NAME_FEMALE.toString() : ImageFiles.NO_NAME_MALE.toString();
imageview.setImage(new Image(path));
setGraphic(imageview);
}
}
}

color row tableview not change javafx

I have a tableView with text message change color in term of a type message.
I have the following code :
tableViewErreur.setRowFactory(param -> new TableRow<BoErreur>() {
#Override
protected void updateItem(BoErreur paramT, boolean empty) {
super.updateItem(paramT, empty);
if (!isEmpty() && paramT != null) {
switch (paramT.getNiveauErreur()) {
case 0:
setId(ConstantsUI.CSS_ERREUR_INFO);
break;
case 1:
setId(ConstantsUI.CSS_ERREUR);
break;
default:
break;
}
tableViewErreur.refresh();
}
}
});
And I have the following code to have a wrap text in my cell :
tableColumnErreur.setCellFactory(new Callback<TableColumn<BoErreur, String>, TableCell<BoErreur, String>>() {
#Override
public TableCell<BoErreur, String> call(TableColumn<BoErreur, String> arg0) {
return new TableCell<BoErreur, String>() {
private Text text;
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!isEmpty()) {
text = new Text(item.toString());
text.setWrappingWidth(tableColumnErreur.getWidth());
this.setWrapText(true);
setGraphic(text);
}
}
};
}
});
The problem is that my second code remove the cell's color. And I need to make :
switch (paramT.getNiveauErreur())
in the RowFactory to determinate my row's color.
Help please,
Thanks.
If you want to make a different colors in odd and even rows you need only to use this code in your .css file.
.table-row-cell:odd{
//add color here
}
.table-row-cell:even{
//add color here
}

How to easy customize JavaFX ListCell background color while selecting and unselecting cells

I have the following problem:
I have ListView<ViewsRecord> where ViewsRecord has property int favorites.
If favorites > 0 it's in favorites, and if favorites == 0 it's a regular row (not favorite).
Now what I want to do is:
When user selects cells (in multiple select mode) those cells will have default selected background (like: -fx-background-color: -fx-selection-bar)
When cells aren't selected by the user:
2.1. if cell isn't in favorites it has regular bacground (ex. white)
2.2. if cell is in favorites it has green background
So far I came up with this solution, but it's ugly code, and I wonder if there's easiest way to do that.
Further more, I had to use setUserData() to check if cell should be selected or not, otherwise during the list scroll, or select - cells had randomly changed their colors. (I assume it's because reusable objects are stored in memory and updateItem() isn't always fired).
Here's my code:
list.setCellFactory(new Callback<ListView<ViewsRecord>, ListCell<ViewsRecord>>(){
#Override
public ListCell<ViewsRecord> call(ListView<ViewsRecord> param) {
ListCell<ViewsRecord> cell = new ListCell<ViewsRecord>(){
#Override
protected void updateItem(ViewsRecord item, boolean empty) {
super.updateItem(item, empty);
if(!empty){
if(item.getFavorites() > 0){ //favorite view
if(!isSelected()){
setStyle("-fx-background-color: darkseagreen;");
}
setUserData(new Integer(1));
} else { //normal view
if(!isSelected()){
setStyle("-fx-background-color: white;");
}
setUserData(new Integer(0));
}
setText(item.toString());
} else { //empty view
setText(null);
setStyle("-fx-background-color: white;");
setUserData(new Integer(0));
}
}
};
//fix bacground color when cell is selected
cell.selectedProperty().addListener( (obsVal, oldVal, newVal) -> {
if(newVal){
cell.setStyle("-fx-background-color: -fx-selection-bar;");
} else {
if((Integer)cell.getUserData() == 1){ //favorite
cell.setStyle("-fx-background-color: darkseagreen;");
} else { //normal
cell.setStyle("-fx-background-color: white;");
}
}
});
return cell;
}
});
EDIT
Thanks to jns I've managed to simplify the code.
Current version:
list.setCellFactory(new Callback<ListView<ViewsRecord>, ListCell<ViewsRecord>>(){
#Override
public ListCell<ViewsRecord> call(ListView<ViewsRecord> param) {
final PseudoClass FAVORITE_PSEUDO_CLASS = PseudoClass.getPseudoClass("favorite");
ListCell<ViewsRecord> cell = new ListCell<ViewsRecord>(){
#Override
protected void updateItem(ViewsRecord item, boolean empty) {
if(!empty){
//favorite or not, and not selected
pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, (item.getFavorites() > 0) && !isSelected());
setText(item.toString());
} else {
setText(null);
//empty
pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, false);
}
super.updateItem(item, empty);
}
};
cell.selectedProperty().addListener( (obsVal, oldVal, newVal) -> {
if(newVal){
//selected
cell.pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, false);
} else {
//favorite or not
cell.pseudoClassStateChanged(FAVORITE_PSEUDO_CLASS, cell.getItem().getFavorites() > 0);
}
});
return cell;
}
});
You could use a PseudoClass for styling the listcell according to the favorite property.
public class YourListCell extends ListCell<ViewsRecord> {
private static PseudoClass FAVORITE_PSEUDO_CLASS = PseudoClass.getPseudoClass("favorite");
#Override
protected void updateItem(ViewsRecord item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
boolean isFavorite = item > 0;
pseudoClassStateChanged(FAVOURITE_PSEUDO_CLASS, isFavorite);
...
}
}
This allows you to style your listcell via css in your stylesheet:
.list-cell:favorite {
-fx-background-color: darkseagreen;
}
ListCell contains a getItem() method. Thus you can get access to your data object. Furthermore you can use its PseudoClass empty and selected to transfer more of the style handling to the stylesheet:
.list-cell, .list-cell:empty {
-fx-background-color: white
}
.list-cell:favorite{
-fx-background-color: darkseagreen;
}
.list-cell:selected {
-fx-background-color: -fx-selection-bar;
}

Resources