JavaFX: Make comboBox cell to change disable state dynamically - javafx

Except for the first item, all items of a comboBox are initially disabled (I used setCellFactory to accomplish this).
If I click on the option 0, I want for it to unlock option 1 and so on.
I tried to use some boolean variables inside a comboBox Listener but it seems like the setCellFactory is called only once. Is this correct?
If so, how could I achieve what I want?
SSCCE below adapted from here
Main.java
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
public class Main extends Application {
boolean isZeroLocked = false;
boolean isOneLocked = true;
boolean isTwoLocked = true;
boolean isThreeLocked = true;
#Override
public void start(Stage stage) throws Exception {
ComboBox<Integer> box = new ComboBox<Integer>();
ObservableList<Integer> values = FXCollections.observableArrayList(0,1,2,3);
box.setItems(values);
box.getSelectionModel().selectedIndexProperty().addListener((observable,oldValue,newValue)->{
System.out.println(newValue + " was clicked. The next option will be unlocked.");
if(newValue.intValue() == 0)
isOneLocked = false;
if(newValue.intValue() == 1)
isTwoLocked = false;
if(newValue.intValue() == 2)
isThreeLocked = false;
});
box.setCellFactory(lv -> new ListCell<Integer>() {
#Override
public void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
setText(item.toString());
if(item.intValue() == 0)
setDisable(isZeroLocked);
if(item.intValue() == 1)
setDisable(isOneLocked);
if(item.intValue() == 2)
setDisable(isTwoLocked);
if(item.intValue() == 3)
setDisable(isThreeLocked);
}
}
});
Scene scene = new Scene(new Group(box));
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
application.css
.combo-box-popup .list-cell:disabled {
-fx-opacity: 0.4 ;
}

I created an Object called CustomNumber to keep up with the disabled property.
Key code:
This code sets the ComboBox's text and enables its cell.
Callback<ListView<CustomNumber>, ListCell<CustomNumber>> factory = lv -> new ListCell<CustomNumber>() {
#Override
protected void updateItem(CustomNumber item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
System.out.println(item.getNum());
setText(Integer.toString(item.getNum()));
setDisable(item.isDisable());
}
}
};
This code gets the cell below the clicked cell and updates its disable property
box.getSelectionModel().selectedIndexProperty().addListener((observable,oldValue,newValue)->{
if(newValue.intValue() + 1 < box.getItems().size())
{
CustomNumber tempCustomNumber = (CustomNumber)box.getItems().get(newValue.intValue() + 1);
tempCustomNumber.setDisable(false);
System.out.println(tempCustomNumber.getNum() + " " + tempCustomNumber.isDisable() + " was unlocked.");
box.getItems().set(newValue.intValue() + 1, tempCustomNumber);
}
});
Full Code:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
ComboBox<CustomNumber> box = new ComboBox();
List<CustomNumber> customNumbers = new ArrayList();
customNumbers.add(new CustomNumber(0, false));
customNumbers.add(new CustomNumber(1, true));
customNumbers.add(new CustomNumber(2, true));
customNumbers.add(new CustomNumber(3, true));
ObservableList<CustomNumber> values = FXCollections.observableArrayList(customNumbers);
box.setItems(values);
Callback<ListView<CustomNumber>, ListCell<CustomNumber>> factory = lv -> new ListCell<CustomNumber>() {
#Override
protected void updateItem(CustomNumber item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
System.out.println(item.getNum());
setText(Integer.toString(item.getNum()));
setDisable(item.isDisable());
}
}
};
box.setCellFactory(factory);
box.setButtonCell(factory.call(null));
box.getSelectionModel().selectedIndexProperty().addListener((observable,oldValue,newValue)->{
if(newValue.intValue() + 1 < box.getItems().size())
{
CustomNumber tempCustomNumber = (CustomNumber)box.getItems().get(newValue.intValue() + 1);
tempCustomNumber.setDisable(false);
System.out.println(tempCustomNumber.getNum() + " " + tempCustomNumber.isDisable() + " was unlocked.");
box.getItems().set(newValue.intValue() + 1, tempCustomNumber);
}
});
Scene scene = new Scene(new Group(box));
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
CustomNumber
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javafxapplication9;
/**
*
* #author sedrick
*/
public class CustomNumber {
private int num;
private boolean disable;
public CustomNumber(int num, boolean disable) {
this.num = num;
this.disable = disable;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public boolean isDisable() {
return disable;
}
public void setDisable(boolean isDisable) {
this.disable = isDisable;
}
}

Related

JavaFx treetableview setStyle for rows with NO children

In TreeTableView I need to find and setStyle rows with NO children.
In below code example, problematic code is in method: markRows.
public class Controller {
public TreeTableView<MyTreeObject> fuses_ttv;
private ArrayList<MyTreeObject> data = new ArrayList<>();
private void createTreeTableView(){}
private void markRows(){
fuses_ttv.setRowFactory(row -> new TreeTableRow<MyTreeObject>(){
#Override
protected void updateItem(MyTreeObject item, boolean empty) {
super.updateItem(item, empty);
if (item==null){
setStyle(null);
} else if (item.getType().equals("FRC")){
setStyle("-fx-background-color: lightslategray;");
} else if(item.getType().equals("wire")){
setStyle("-fx-background-color: lightyellow;");
} //***** else if (ROW HAS NOW CHILDREN) - HOW TO DO IT????? ******
}
});
}
}
Like in picture below - rows with SLOT "A1" and "A2" have no children.
How to identify such rows?
Thanks in advance for any help.
In JavaFX 19 and later you can do:
fuses_ttv.setRowFactory(row -> new TreeTableRow<MyTreeObject>(){
{
treeItemProperty().flatMap(TreeItem::leafProperty)
.orElse(false)
.addListener((obs, wasLeaf, isLeaf) -> {
if (isLeaf) {
// set style for leaf (no children)
} else {
// set style for non-leaf (has children)
}
});
}
#Override
protected void updateItem(MyTreeObject item, boolean empty) {
super.updateItem(item, empty);
if (item==null){
setStyle(null);
} else if (item.getType().equals("FRC")){
setStyle("-fx-background-color: lightslategray;");
} else if(item.getType().equals("wire")){
setStyle("-fx-background-color: lightyellow;");
} //***** else if (ROW HAS NOW CHILDREN) - HOW TO DO IT????? ******
}
});
I would actually recommend setting custom PseudoClasses, and an external style sheet, instead of using inline styles.
Here is a complete working example:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class TreeTableStyleExample extends Application {
private int itemCount ;
#Override
public void start(Stage stage) throws IOException {
TreeTableView<Integer> table = new TreeTableView<>();
TreeTableColumn<Integer, Number> column = new TreeTableColumn<>("Item");
table.getColumns().add(column);
column.setCellValueFactory(data -> new SimpleIntegerProperty(data.getValue().getValue()));
column.setCellFactory(ttv -> new TreeTableCell<>() {
#Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
} else {
setText("Item "+item);
}
}
});
PseudoClass leaf = PseudoClass.getPseudoClass("leaf");
PseudoClass odd = PseudoClass.getPseudoClass("odd-value");
PseudoClass even = PseudoClass.getPseudoClass("even-value");
table.setRowFactory( ttv -> new TreeTableRow<>() {
{
treeItemProperty().flatMap(TreeItem::leafProperty).orElse(false)
.addListener((obs, wasLeaf, isNowLeaf) -> pseudoClassStateChanged(leaf, isNowLeaf));
}
#Override
protected void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
pseudoClassStateChanged(odd, false);
pseudoClassStateChanged(even, false);
} else {
pseudoClassStateChanged(odd, item % 2 == 1);
pseudoClassStateChanged(even, item % 2 == 0);
}
}
});
table.setRoot(buildTable(20));
Button add = new Button("Add item");
add.disableProperty().bind(Bindings.isEmpty(table.getSelectionModel().getSelectedItems()));
add.setOnAction(e -> {
TreeItem<Integer> treeItem = new TreeItem<>(++itemCount);
treeItem.setExpanded(true);
table.getSelectionModel().getSelectedItem().getChildren().add(treeItem);
});
Button remove = new Button("Remove");
remove.disableProperty().bind(Bindings.isEmpty(table.getSelectionModel().getSelectedItems())
.or(Bindings.equal(table.getSelectionModel().selectedItemProperty(), table.getRoot())));
remove.setOnAction(e -> {
TreeItem<Integer> selection = table.getSelectionModel().getSelectedItem();
selection.getParent().getChildren().remove(selection);
});
HBox controls = new HBox(5, add, remove);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(5));
BorderPane root = new BorderPane(table);
root.setBottom(controls);
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
private TreeItem<Integer> buildTable(int numItems) {
Random rng = new Random();
TreeItem<Integer> root = new TreeItem<>(1);
root.setExpanded(true);
List<TreeItem> items = new ArrayList<>();
items.add(root);
for (itemCount = 2; itemCount <= numItems ; itemCount++) {
TreeItem<Integer> item = new TreeItem<>(itemCount);
item.setExpanded(true);
items.get(rng.nextInt(items.size())).getChildren().add(item);
items.add(item);
}
return root ;
}
public static void main(String[] args) {
launch();
}
}
with style.css:
.tree-table-row-cell:odd-value {
-fx-background: lightslategray ;
}
.tree-table-row-cell:even-value {
-fx-background: lightyellow;
}
.tree-table-row-cell:leaf {
-fx-background: lightgreen ;
}
Sample output:

JavaFx TableCellEditor

I have JavaFx TableView and I permit user to enter some data in the table and retrieve what a user entered in the table for this reason I create also ArrayList of TextFields and use the following code but the size of the ArrayList should be 3 in my case but I found the size 7, what's wrong?
Edit the full code here
import java.util.ArrayList;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
public class TestTableView extends Application
{
private final TableView <ProductOpeningBalance> tableProductOpeningBalance = new TableView();
private final Scene scene = new Scene(tableProductOpeningBalance, 400, 200);
private final TableColumn productQuantity = new TableColumn("Product Quantity");
private final ObservableList <ProductOpeningBalance> data = FXCollections.observableArrayList(
new ProductOpeningBalance("0"),
new ProductOpeningBalance("0"));
private final ArrayList <TextField> txtProductQuantity = new ArrayList <> ();
#Override
public void start(Stage stage)
{
productQuantity.impl_setReorderable(false);
productQuantity.setEditable(true);
productQuantity.setCellValueFactory(new PropertyValueFactory("ProductQuantity"));
productQuantity.setCellFactory(column -> new TableCell()
{
#Override
public void startEdit()
{
if(!isEmpty())
{
super.startEdit();
createTextField();
setText(null);
setGraphic(txtProductQuantity.get(txtProductQuantity.size() - 1));
txtProductQuantity.get(txtProductQuantity.size() - 1).selectAll();
}
}
#Override
public void cancelEdit()
{
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
public void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
if(empty)
{
setText(null);
setGraphic(null);
}
else
{
if(isEditing())
{
if(txtProductQuantity.get(txtProductQuantity.size() - 1) != null)
{
txtProductQuantity.get(txtProductQuantity.size() - 1).setText(getString());
}
setText(null);
setGraphic(txtProductQuantity.get(txtProductQuantity.size() - 1));
}
else
{
setText(getString());
setGraphic(null);
}
}
}
private void createTextField()
{
txtProductQuantity.add(new TextField(getString()));
txtProductQuantity.get(txtProductQuantity.size() - 1).
setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
txtProductQuantity.get(txtProductQuantity.size() - 1).setAlignment(Pos.BASELINE_RIGHT);
txtProductQuantity.get(txtProductQuantity.size() - 1).focusedProperty().
addListener((ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) ->
{
if(!arg2)
{
commitEdit(txtProductQuantity.get(txtProductQuantity.size() - 1).getText());
}
});
}
private String getString()
{
return getItem() == null ? "" : getItem().toString();
}
});
tableProductOpeningBalance.setEditable(true);
tableProductOpeningBalance.getColumns().addAll(productQuantity);
tableProductOpeningBalance.setItems(data);
stage.setScene(scene);
stage.show();
}
public class ProductOpeningBalance
{
private final SimpleStringProperty ProductQuantity;
public ProductOpeningBalance(String productQuantity)
{
this.ProductQuantity = new SimpleStringProperty(productQuantity);
}
public void setProductQuantity(String productQuantity)
{
ProductQuantity.set(productQuantity);
}
public String getProductQuantity()
{
return ProductQuantity.get();
}
}
}
Solution finally:
this code help me to find what I need after spent a lot of time in searching and trying a lot of methods
purchaseItemPrice.setCellFactory(column -> new TableCell()
{
#Override
public void startEdit()
{
if(!isEmpty())
{
if(txtPurchaseItemPrice.size() < data.size() && getGraphic() == null)
{
super.startEdit();
txtPurchaseItemPrice.add(new TextField());
txtPurchaseItemPrice.get(txtPurchaseItemPrice.size() - 1).setAlignment(Pos.BASELINE_RIGHT);
setGraphic(txtPurchaseItemPrice.get(txtPurchaseItemPrice.size() - 1));
}
}
}
public void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
if(!empty)
{
if(isEditing())
{
setGraphic(txtPurchaseItemPrice.get(txtPurchaseItemPrice.size() - 1));
}
}
}
});

Jfx/TableColumn.setCellFactory with multiple colors in one TableCell

In a JavaFx/TableView, is it possible to get a TableCell with multiple colors for the text ? I've tried the following code using 'Text' where each character would be in RED/BLUE/RED/BLUE... but the cells remain black.
(...)
tableColumn.setCellFactory(tc -> new TableCell<MyObject, String>() {
#Override
protected void updateItem(final String item, boolean empty) {
super.updateItem(item, empty);
if(item==null) return;
this.setText(item);
final List<Text> L=new ArrayList<>(item.length());
for(int i=0;i< item.length();++i) {
final Text txt=new Text(String.valueOf(item.charAt(i)));
txt.setStroke(i%2==0?Color.RED:Color.BLUE);
L.add(txt);
}
this.getChildren().setAll(L);
}
});
(...)
Is there any way to achieve this ? Thanks.
Create a TextFlow to hold the Text instances and set it as the cell's graphic. Note also that you have a bug (that will become apparent if you remove items from the table's list, or possibly if you scroll): you need to clear the text and graphic if the cell is empty.
tableColumn.setCellFactory(tc -> new TableCell<MyObject, String>() {
final TextFlow textFlow = new TextFlow();
#Override
protected void updateItem(final String item, boolean empty) {
super.updateItem(item, empty);
if(item==null) {
setText(null);
setGraphic(null);
return ;
}
this.setText(item);
final List<Text> L=new ArrayList<>(item.length());
for(int i=0;i< item.length();++i) {
final Text txt=new Text(String.valueOf(item.charAt(i)));
txt.setStroke(i%2==0?Color.RED:Color.BLUE);
L.add(txt);
}
textFlow.getChildren().setAll(L);
setGraphic(textFlow);
}
});
Here's a SSCCE:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class TableWithAlternateColorText extends Application {
private final Random rng = new Random();
private final String[] bases = "ACGT".split("") ;
#Override
public void start(Stage primaryStage) {
TableView<StringProperty> table = new TableView<>();
TableColumn<StringProperty, String> col = new TableColumn<>("Sequence");
col.setCellValueFactory(CellDataFeatures::getValue);
col.setCellFactory(tc -> new TableCell<StringProperty, String>() {
final TextFlow textFlow = new TextFlow();
#Override
protected void updateItem(final String item, boolean empty) {
super.updateItem(item, empty);
if(item==null) {
setText(null);
setGraphic(null);
return ;
}
List<Text> texts = new ArrayList<>();
for(int i=0;i< item.length();++i) {
char base = item.charAt(i);
final Text txt=new Text(String.valueOf(base));
txt.setStroke(isPyrimidine(base) ? Color.RED : Color.BLUE);
texts.add(txt);
}
textFlow.getChildren().setAll(texts);
setGraphic(textFlow);
setPrefHeight(textFlow.prefHeight(-1));
}
});
table.getColumns().add(col);
for (int i = 0 ; i < 100 ; i++) {
table.getItems().add(new SimpleStringProperty(randomSequence(20)));
}
primaryStage.setScene(new Scene(table, 600, 600));
primaryStage.show();
}
private boolean isPyrimidine(char base) {
return base == 'C' || base == 'T' ;
}
private String randomSequence(int seqLength) {
return rng.ints(seqLength, 0, bases.length)
.mapToObj(i -> bases[i])
.collect(Collectors.joining());
}
public static void main(String[] args) {
launch(args);
}
}

javafx treeitem file path

i have a question for this sample code.
import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CopyOfTreeViewSample extends Application {
public static Image folderCollapseImage=new Image(ClassLoader.getSystemResourceAsStream("treeviewsample/folder.png"));
public static Image folderExpandImage=new Image(ClassLoader.getSystemResourceAsStream("treeviewsample/folder-open.png"));
public static Image fileImage=new Image(ClassLoader.getSystemResourceAsStream("treeviewsample/text-x-generic.png"));
public static Image rootImage = new Image(ClassLoader.getSystemResourceAsStream("treeviewsample/computer.png"));
private TreeView treeView;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Sample");
stage.setWidth(300);
stage.setHeight(500);
VBox vbox = new VBox();
vbox.setLayoutX(20);
vbox.setLayoutY(20);
TreeItem<String> root = new SimpleFileTreeItem(Paths.get("C:\\Users\\Jake"), true);
treeView = new TreeView<String>(root);
Button b = new Button("Change");
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
// TODO Auto-generated method stub
////// How do i this section write code?//////
}
});
vbox.getChildren().addAll(treeView,b);
vbox.setSpacing(10);
((Group) scene.getRoot()).getChildren().add(vbox);
stage.setScene(scene);
stage.show();
}
public class SimpleFileTreeItem extends TreeItem<String> {
private boolean isRoot = false;
private boolean isFirstTimeChildren = true;
private boolean isFirstTimeLeaf = true;
private boolean isLeaf;
private boolean isDirectory;
public boolean isDirectory(){return(this.isDirectory);}
private String fullPath;
public String getFullPath(){return(this.fullPath);}
public SimpleFileTreeItem(Path f, Boolean flag) {
super(f.toString());
fullPath = f.toString();
isRoot = flag;
if(!isRoot)
{
if(Files.isDirectory(f))
{
isDirectory = true;
this.setGraphic(new ImageView(folderCollapseImage));
}
else
{
isDirectory = false;
this.setGraphic(new ImageView(fileImage));
}
//set the value
if(!fullPath.endsWith(File.separator)){
String value=f.toString();
int indexOf=value.lastIndexOf(File.separator);
if(indexOf>0){
this.setValue(value.substring(indexOf+1));
}else{
this.setValue(value);
}
}
this.addEventHandler(TreeItem.branchExpandedEvent(),new EventHandler(){
#Override
public void handle(Event e){
SimpleFileTreeItem source=(SimpleFileTreeItem)e.getSource();
if(source.isDirectory()&&source.isExpanded()){
ImageView iv=(ImageView)source.getGraphic();
iv.setImage(folderExpandImage);
}
}
});
this.addEventHandler(TreeItem.branchCollapsedEvent(),new EventHandler(){
#Override
public void handle(Event e){
SimpleFileTreeItem source=(SimpleFileTreeItem)e.getSource();
if(source.isDirectory()&&!source.isExpanded()){
ImageView iv=(ImageView)source.getGraphic();
iv.setImage(folderCollapseImage);
}
}
});
}
else
{
this.setExpanded(true);
if(Files.isDirectory(f))
{
isDirectory = true;
this.setGraphic(new ImageView(rootImage));
}
else
{
isDirectory = false;
this.setGraphic(new ImageView(fileImage));
}
}
}
#Override
public ObservableList<TreeItem<String>> getChildren() {
if (isFirstTimeChildren) {
isFirstTimeChildren = false;
/*
* First getChildren() call, so we actually go off and determine the
* children of the File contained in this TreeItem.
*/
super.getChildren().setAll(buildChildren(this));
}
return super.getChildren();
}
#Override
public boolean isLeaf() {
if (isFirstTimeLeaf) {
isFirstTimeLeaf = false;
File f = new File(fullPath);
isLeaf = f.isFile();
}
return isLeaf;
}
/**
* Returning a collection of type ObservableList containing TreeItems, which
* represent all children available in handed TreeItem.
*
* #param TreeItem
* the root node from which children a collection of TreeItem
* should be created.
* #return an ObservableList<TreeItem<File>> containing TreeItems, which
* represent all children available in handed TreeItem. If the
* handed TreeItem is a leaf, an empty list is returned.
*/
private ObservableList<TreeItem<String>> buildChildren(TreeItem<String> TreeItem) {
File f = new File(fullPath);
if (f != null && f.isDirectory()) {
File[] files = f.listFiles();
if (files != null) {
ObservableList<TreeItem<String>> children = FXCollections
.observableArrayList();
for (File childFile : files) {
children.add(new SimpleFileTreeItem(childFile.toPath(), false));
}
return children;
}
}
return FXCollections.emptyObservableList();
}
}
I want to select the file that corresponds to the path when the button is pressed .
my eclipse project path is c:\java\samplecode.
I was trying to solve by using Absolutepath the result is c:\java\samplecode\samplefile.txt
i want this path.(c:\Users\jake\samplefile.txt)
Thank you for advice and tips.
Using the setup you have, you should be able to do
b.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
SimpleFileTreeItem<String> selectedItem = (SimpleFileTreeItem)treeView.getSelectionModel().getSelectedItem();
if (selectedItem != null) {
String pathString = selectedItem.getFullPath();
}
}
});
(If you also do
b.disableProperty().bind(
Bindings.isNull(treeView.getSelectionModel().selectedItemProperty()));
then you can safely skip the check for null in the handler.)
I think a better approach would be to make SimpleFileTreeItem a TreeItem<Path>. Then you just keep the Path as the value of the tree item, and you can use a cell factory to just display the file name.
Here is an example using this approach. I took out the images (so it can be executed without relying on external resources) and also a lot of the other unnecessary code.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CopyOfTreeViewSample extends Application {
private TreeView<Path> treeView;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
stage.setTitle("Sample");
stage.setWidth(300);
stage.setHeight(500);
VBox vbox = new VBox();
vbox.setPadding(new Insets(20));
TreeItem<Path> root = new SimpleFileTreeItem(
Paths.get(System.getProperty("user.home")));
root.setExpanded(true);
treeView = new TreeView<Path>(root);
treeView.setCellFactory(treeView -> new TreeCell<Path>() {
#Override
public void updateItem(Path path, boolean empty) {
super.updateItem(path, empty);
if (empty) {
setText(null);
} else {
setText(path.getFileName().toString());
}
}
});
Button b = new Button("Change");
b.disableProperty().bind(Bindings.isNull(treeView.getSelectionModel().selectedItemProperty()));
b.setOnAction(event -> {
Path selectedPath = treeView.getSelectionModel().getSelectedItem().getValue() ;
// do something with selectedPath...
System.out.println(selectedPath);
});
vbox.getChildren().addAll(treeView, b);
vbox.setSpacing(10);
Scene scene = new Scene(vbox);
stage.setScene(scene);
stage.show();
}
public class SimpleFileTreeItem extends TreeItem<Path> {
private boolean isFirstTimeChildren = true;
private boolean isFirstTimeLeaf = true;
private boolean isLeaf;
public boolean isDirectory() {
return Files.isDirectory(getValue());
}
public SimpleFileTreeItem(Path f) {
super(f);
}
#Override
public ObservableList<TreeItem<Path>> getChildren() {
if (isFirstTimeChildren) {
isFirstTimeChildren = false;
/*
* First getChildren() call, so we actually go off and determine
* the children of the File contained in this TreeItem.
*/
super.getChildren().setAll(buildChildren());
}
return super.getChildren();
}
#Override
public boolean isLeaf() {
if (isFirstTimeLeaf) {
isFirstTimeLeaf = false;
isLeaf = Files.exists(getValue()) && ! Files.isDirectory(getValue());
}
return isLeaf;
}
/**
* Returning a collection of type ObservableList containing TreeItems,
* which represent all children of this TreeITem.
*
*
* #return an ObservableList<TreeItem<File>> containing TreeItems, which
* represent all children available in this TreeItem. If the
* handed TreeItem is a leaf, an empty list is returned.
*/
private ObservableList<TreeItem<Path>> buildChildren() {
if (Files.isDirectory(getValue())) {
try {
return Files.list(getValue())
.map(SimpleFileTreeItem::new)
.collect(Collectors.toCollection(() -> FXCollections.observableArrayList()));
} catch (IOException e) {
e.printStackTrace();
return FXCollections.emptyObservableList();
}
}
return FXCollections.emptyObservableList();
}
}
}

Custom ListCell implements InvalidationListener, repaint components

I have a custom ListCell implemented that contains a BorderPane layout with some components.
The cell registers itself to the item. So when the duration of the item changes the invalidated method is called.
In this method I set the text of the duration label. My problem is now the method is called but the label is not repainted.
I think if setText is called the cell should repaint. It is possible to manually repaint the cell or the Label.?
public static class ListItemCell extends ListCell<MusicListItem> implements InvalidationListener{
private AnchorPane listItem;
private Label artist;
private Label title;
private Label duration;
private BorderPane borderPane;
private FlowPane flowPane;
public ListItemCell() {
initCellLayout();
}
public ListItemCell(final LogicInterfaceFX logic) {
...
}
public void initCellLayout() {
try {
this.listItem = (AnchorPane) FXMLLoader.load(getClass().getResource("/de/roth/jsona/view/themes/" + Config.getInstance().THEME + "/" + "layout_list_item.fxml"));
} catch (Exception e) {
e.printStackTrace();
}
this.borderPane = (BorderPane) listItem.getChildren().get(0);
this.flowPane = (FlowPane) borderPane.getLeft();
this.artist = (Label) flowPane.getChildren().get(0);
this.artist.getStyleClass().add(defaultTextClass);
this.title = (Label) flowPane.getChildren().get(1);
this.title.getStyleClass().add(defaultTextClass);
this.duration = (Label) borderPane.getRight();
this.duration.getStyleClass().add(defaultTextClass);
this.setGraphic(listItem);
}
#Override
public void updateItem(MusicListItem item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
item.addListener(this);
item.durationProperty().addListener(this);
// Duration
this.duration.setText(item.getDuration());
// Artist / Title
if (item.getArtist() != null) {
this.artist.setText(item.getArtist());
this.title.setText(" - " + item.getTitle());
} else {
this.artist.setText("");
this.title.setText(item.getFile().getName());
}
} else {
this.artist.setText("");
this.title.setText("");
this.duration.setText("");
}
}
#Override
public void invalidated(Observable observable) {
System.out.println("INVALIDATE!!!" + getItem().getFile().getAbsolutePath());
this.duration.setText(getItem().getDuration());
}
}
You have a bug in there: you need to make sure you remove listeners from old items when the item is updated. Remember that ListCells are reused, so updateItem(...) is called multiple times during the lifespan of your ListView.
I don't know if that's what is causing it to fail to update. This works for me:
import java.util.Random;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewUpdatableProperties extends Application {
#Override
public void start(Stage primaryStage) {
final ListView<Item> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
final Random rng = new Random();
for (int i=1; i<=20; i++) {
listView.getItems().add(new Item("Item "+i, rng.nextInt(100)));
}
BorderPane root = new BorderPane();
root.setCenter(listView);
listView.setCellFactory(new Callback<ListView<Item>, ListCell<Item>>() {
#Override
public ListCell<Item> call(ListView<Item> param) {
return new ItemListCell();
}
});
HBox controls = new HBox();
controls.setPadding(new Insets(5));
Button incButton = new Button("Increment selected");
incButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for (Item item : listView.getSelectionModel().getSelectedItems()) {
item.increment();
}
}
});
controls.getChildren().add(incButton);
root.setBottom(controls);
Scene scene = new Scene(root, 250, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class ItemListCell extends ListCell<Item> implements InvalidationListener {
private final HBox hbox ;
private final Label nameLabel ;
private final Label valueLabel ;
public ItemListCell() {
hbox = new HBox(5);
nameLabel = new Label();
valueLabel = new Label();
hbox.getChildren().addAll(nameLabel, valueLabel);
setGraphic(hbox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
public void updateItem(Item item, boolean empty) {
Item oldItem = getItem();
if (oldItem != null) {
oldItem.valueProperty().removeListener(this);
}
super.updateItem(item, empty);
if (item != null) {
nameLabel.setText(item.getName());
valueLabel.setText(String.valueOf(item.getValue()));
item.valueProperty().addListener(this);
} else {
nameLabel.setText("");
valueLabel.setText("");
}
}
#Override
public void invalidated(Observable observable) {
final int value = getItem().getValue();
System.out.println("Invalidated: item is "+getItem().getName() + " with value "+value);
valueLabel.setText(String.valueOf(value));
}
}
public static class Item {
public Item(String name, int value) {
setName(name);
setValue(value);
}
private final StringProperty name = new SimpleStringProperty(this, "name");
public StringProperty nameProperty() {
return name ;
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
public IntegerProperty valueProperty() {
return value ;
}
public int getValue() {
return value.get();
}
public void setValue(int value) {
this.value.set(value);
}
public void increment() {
value.set(value.get()+1);
}
}
public static void main(String[] args) {
launch(args);
}
}
As stated in the other answer, there is no repaint() method in JavaFX. If you wire things up correctly, when the properties are invalidated, it will know to repaint.
JavaFX uses a "retained mode" rendering model whereas Swing uses an "immediate mode".
See: Retained Mode Versus Immediate Mode (Windows)
So, you don't have a direct repaint() method.

Resources