JavaFX tableview hiding a selected row - javafx

In my JavaFX application I am trying to hide the selected rows in the tableview. I used CSS as below.
.hideRow
{
-fx-cell-size: 0px;
-fx-border-width: 0;
}
I used the below row factory for the achieving the same.
import java.util.Collections;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.util.Callback;
/
**
* This is the table view row style factory class. * This class is responsible
* for performing style changes for enable,disable,hide and un hide of the rows.
*
* #param <T>
*/
public class StyleChangingRowFactory<T> implements
Callback<TableView<T>, TableRow<T>> {
private TableRow rowSelected;
private final String styleClass;
private final ObservableList<Integer> styledRowIndices;
private final Callback<TableView<T>, TableRow<T>> baseFactory;
/**
* Construct a <code>StyleChangingRowFactory</code>, specifying the name of
* the style class that will be applied to rows determined by
* <code>getStyledRowIndices</code> and a base factory to create the
* <code>TableRow</code>. If <code>baseFactory</code> is <code>null</code>,
* default table rows will be created.
*
* #param styleClass The name of the style class that will be applied to
* specified rows.
* #param baseFactory A factory for creating the rows. If null, default
* <code>TableRow<T></code>s will be created using the default
* <code>TableRow</code> constructor.
*/
public StyleChangingRowFactory(String styleClass, Callback<TableView<T>, TableRow<T>> baseFactory) {
this.styleClass = styleClass;
this.baseFactory = baseFactory;
this.styledRowIndices = FXCollections.observableArrayList();
}
/**
* Construct a <code>StyleChangingRowFactory</code>, which applies
* <code>styleClass</code> to the rows determined by
* <code>getStyledRowIndices</code>, and using default
* <code>TableRow</code>s.
*
* #param styleClass
*/
public StyleChangingRowFactory(String styleClass) {
this(styleClass, null);
}
#Override
public TableRow<T> call(final TableView<T> tableView) {
final TableRow<T> row;
if (baseFactory == null) {
row = new TableRow<>();
} else {
row = baseFactory.call(tableView);
}
row.setOnDragEntered(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent t) {
tableView.getSelectionModel().clearSelection();
tableView.getSelectionModel().selectRange(rowSelected.getIndex(), row.getIndex());
}
});
row.setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
rowSelected = row;
Dragboard db = row.getTableView().startDragAndDrop(TransferMode.LINK);
ClipboardContent content = new ClipboardContent();
content.put(DataFormat.PLAIN_TEXT, "XData");
db.setContent(content);
tableView.getSelectionModel().clearSelection();
t.consume();
}
});
row.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
rowSelected = row;
final int index = row.getIndex();
if (event.getButton() == MouseButton.SECONDARY && index >= 0 && !tableView.getSelectionModel().isSelected(index)) {
tableView.getSelectionModel().clearSelection();
}
}
});
row.indexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> obs,
Number oldValue, Number newValue) {
updateStyleClass(row);
}
});
styledRowIndices.addListener(new ListChangeListener<Integer>() {
#Override
public void onChanged(Change<? extends Integer> change) {
updateStyleClass(row);
tableView.getColumns().get(0).setVisible(false);
tableView.getColumns().get(0).setVisible(true);
}
});
return row;
}
/**
*
* #return The list of indices of the rows to which <code>styleClass</code>
* will be applied. Changes to the content of this list will result in the
* style class being immediately updated on rows whose indices are either
* added to or removed from this list.
*/
public ObservableList<Integer> getStyledRowIndices() {
return styledRowIndices;
}
private void updateStyleClass(TableRow<T> row) {
final ObservableList<String> rowStyleClasses = row.getStyleClass();
if (styledRowIndices.contains(row.getIndex())) {
if (!rowStyleClasses.contains(styleClass)) {
rowStyleClasses.add(styleClass);
}
} else {
// remove all occurrences of styleClass:
rowStyleClasses.removeAll(Collections.singletonList(styleClass));
}
}
}
I declared the row factory as
StyleChangingRowFactory rowFactory = new StyleChangingRowFactory<>("hideRow");
I have two pop up menus in my table, they are hide and unhide.
In my controller I used the below methods with implementation for those menus.
private void hidePressed() {
rowFactory.getHiddenRowIndices().addAll(cfiTableView.getSelectionModel().getSelectedIndices());
}
private void unHidePressed() {
rowFactory.getHiddenRowIndices().removeAll(cfiTableView.getSelectionModel().getSelectedIndices());
}
The problem is when I hide the selected row(s). It is also hiding some other rows in the table view. Please help me to solve this issue.

Related

JavaFX TableView Context menu get the selected ID

I have a TableView where I display data from my database. The TableView itself is not hard-coded on a specific table but it adds the columns and the data instead. I added Context menu, so when the user right clicks on an item, it deletes it. It works fine but I don't know how to get the ID which is the first column. Note that I don't want the selected index but the ID from the tableView (first column).
The questioned snippet:
removeMenuItem.setOnAction(event -> {
System.out.println(row.getItem()); // HERE
this.tableView.getItems().remove(row.getItem());
});
Basically, row.getItem() returns the record:
[2, Name, Stuff, Test Test, Category]
The question is how do I get the ID (in this case 2) or the name?
Full code:
package controllers;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.util.Callback;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TableController {
#FXML
private Label descriptionLabel;
#FXML
private TableView tableView;
public void setTableResultSet(String label, ResultSet resultSet) throws SQLException {
// Create context menu
createContextMenu();
// Set label text
descriptionLabel.setText(label);
// Resolve columns
ObservableList<ObservableList> data = resolveTableColumns(resultSet);
// Add records
addTableRecords(resultSet, data);
}
/**
* Create a context menu responsible for removing items.
*/
private void createContextMenu() {
tableView.setRowFactory(tableView -> {
final TableRow row = new TableRow();
final ContextMenu contextMenu = new ContextMenu();
final MenuItem removeMenuItem = new MenuItem("Remove");
removeMenuItem.setOnAction(event -> {
System.out.println(row.getItem()); // HERE
this.tableView.getItems().remove(row.getItem());
});
contextMenu.getItems().add(removeMenuItem);
// Set context menu on row, but use a binding to make it only show for non-empty rows:
row.contextMenuProperty().bind(
Bindings.when(row.emptyProperty())
.then((ContextMenu) null)
.otherwise(contextMenu)
);
return row;
});
}
/**
* Resolves table columns based on a ResultSet.
*
* #param resultSet the ResultSet.
* #return
* #throws SQLException
*/
private ObservableList<ObservableList> resolveTableColumns(ResultSet resultSet) throws SQLException {
ObservableList<ObservableList> data = FXCollections.observableArrayList();
for (int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) {
final int j = i;
TableColumn col = new TableColumn(resultSet.getMetaData().getColumnName(i + 1));
col.setCellValueFactory((Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>) param -> {
return new SimpleStringProperty(param.getValue().get(j).toString());
});
tableView.getColumns().add(col);
}
return data;
}
/**
* Adds table data from a ResultSet.
*
* #param resultSet the ResultSet.
* #param data the data.
* #throws SQLException
*/
private void addTableRecords(ResultSet resultSet, ObservableList<ObservableList> data) throws SQLException {
while (resultSet.next()) {
ObservableList<String> row = FXCollections.observableArrayList();
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
row.add(resultSet.getString(i));
}
data.add(row);
}
tableView.setItems(data);
}
}
Edit based on #James_D's comment:
package controllers;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.util.Callback;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TableController {
#FXML
private Label descriptionLabel;
#FXML
private TableView<ObservableList<String>> tableView;
public void setTableResultSet(String label, ResultSet resultSet) throws SQLException {
// Create context menu
createContextMenu();
// Set label text
descriptionLabel.setText(label);
// Resolve columns
ObservableList<ObservableList<String>> data = resolveTableColumns(resultSet);
// Add records
addTableRecords(resultSet, data);
}
/**
* Create a context menu responsible for removing items.
*/
private void createContextMenu() {
tableView.setRowFactory(tableView -> {
final TableRow<ObservableList<String>> row = new TableRow<>();
final ContextMenu contextMenu = new ContextMenu();
final MenuItem removeMenuItem = new MenuItem("Изтрий");
removeMenuItem.setOnAction(event -> {
System.out.println(row.getItem().get(1));
this.tableView.getItems().remove(row.getItem());
});
contextMenu.getItems().add(removeMenuItem);
// Set context menu on row, but use a binding to make it only show for non-empty rows:
row.contextMenuProperty().bind(
Bindings.when(row.emptyProperty())
.then((ContextMenu) null)
.otherwise(contextMenu)
);
return row;
});
}
/**
* Resolves table columns based on a ResultSet.
*
* #param resultSet the ResultSet.
* #return
* #throws SQLException
*/
private ObservableList<ObservableList<String>> resolveTableColumns(ResultSet resultSet) throws SQLException {
ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
for (int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) {
final int j = i;
TableColumn col = new TableColumn(resultSet.getMetaData().getColumnName(i + 1));
col.setCellValueFactory((Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>) param -> {
return new SimpleStringProperty(param.getValue().get(j).toString());
});
tableView.getColumns().add(col);
}
return data;
}
/**
* Adds table data from a ResultSet.
*
* #param resultSet the ResultSet.
* #param data the data.
* #throws SQLException
*/
private void addTableRecords(ResultSet resultSet, ObservableList<ObservableList<String>> data) throws SQLException {
while (resultSet.next()) {
ObservableList<String> row = FXCollections.observableArrayList();
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
row.add(resultSet.getString(i));
}
data.add(row);
}
tableView.setItems(data);
}
}
Don't use raw types: that way you can access the appropriate methods for the data you retrieve from the table (without have to downcast anywhere).
Specifically, you declare you represent your data as an
ObservableList<ObservableList>, which means that each row is represented as a (raw) ObservableList. Since, looking at your addTableRecords method, each element of a row is a String, the rows should be represented as ObservableList<String>, and the overall data as ObservableList<ObservableList<String>>.
The TableView and associated UI elements should also be parameterized: i.e. you should be using
TableView<ObservableList<String>>
TableColumn<ObservableList<String>, String>
TableRow<ObservableList<String>
If you make these changes, then in your createContextMenu() method you will have
TableRow<ObservableList<String>> row = new TableRow<>();
Consequently the return type of row.getItem() will be ObservableList<String> and you can call methods defined in ObservableList (or more generally, List) on the returned value. Specifically, calling get(...) will return the String in a given cell in the row:
String id = row.getItem().get(0);
String name = row.getItem().get(1);
etc.
Put all together, this looks like
package controllers;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.util.Callback;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TableController {
#FXML
private Label descriptionLabel;
#FXML
private TableView<ObservableList<String>> tableView;
public void setTableResultSet(String label, ResultSet resultSet) throws SQLException {
// Create context menu
createContextMenu();
// Set label text
descriptionLabel.setText(label);
// Resolve columns
ObservableList<ObservableList<String>> data = resolveTableColumns(resultSet);
// Add records
addTableRecords(resultSet, data);
}
/**
* Create a context menu responsible for removing items.
*/
private void createContextMenu() {
tableView.setRowFactory(tableView -> {
final TableRow<ObservableList<String>> row = new TableRow<>();
final ContextMenu contextMenu = new ContextMenu();
final MenuItem removeMenuItem = new MenuItem("Изтрий");
removeMenuItem.setOnAction(event -> {
String id = row.getItem().get(0);
String name = row.getItem().get(1);
// do whatever you need with id and name, etc.
this.tableView.getItems().remove(row.getItem());
});
contextMenu.getItems().add(removeMenuItem);
// Set context menu on row, but use a binding to make it only show for non-empty rows:
row.contextMenuProperty().bind(
Bindings.when(row.emptyProperty())
.then((ContextMenu) null)
.otherwise(contextMenu)
);
return row;
});
}
/**
* Resolves table columns based on a ResultSet.
*
* #param resultSet the ResultSet.
* #return
* #throws SQLException
*/
private ObservableList<ObservableList<String>> resolveTableColumns(ResultSet resultSet) throws SQLException {
ObservableList<ObservableList<String>> data = FXCollections.observableArrayList();
for (int i = 0; i < resultSet.getMetaData().getColumnCount(); i++) {
final int j = i;
TableColumn<ObservableList<String>, String> col = new TableColumn<>(resultSet.getMetaData().getColumnName(i + 1));
col.setCellValueFactory(param ->
new SimpleStringProperty(param.getValue().get(j))
);
tableView.getColumns().add(col);
}
return data;
}
/**
* Adds table data from a ResultSet.
*
* #param resultSet the ResultSet.
* #param data the data.
* #throws SQLException
*/
private void addTableRecords(ResultSet resultSet, ObservableList<ObservableList<String>> data) throws SQLException {
while (resultSet.next()) {
ObservableList<String> row = FXCollections.observableArrayList();
for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
row.add(resultSet.getString(i));
}
data.add(row);
}
tableView.setItems(data);
}
}

how to get a tab through the tab's attribute text in TabPane in javafx?

I want get a tab through the tab's attribute text, but I cannot find a native method. So I writed a class to implement the purpose.
Question:
- I want to know if there is a native method for that purpose?
- Is there a better implementation?
Thanks!
```
package pre.huangjs.tabpane;
import javafx.collections.ListChangeListener;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import java.util.HashMap;
import java.util.List;
/**
* Created by huangjs on 2018/4/9.
*/
public class TabPaneExpansion {
private TabPane tabPane;
private HashMap<String, Tab> tabsMap;
public TabPane getTabPane() {
return tabPane;
}
public void setTabPane(TabPane tabPane) {
this.tabPane = tabPane;
}
public TabPaneExpansion() {
this.tabPane = new TabPane();
this.tabsMap = new HashMap<>();
initial();
}
public TabPaneExpansion(TabPane tabPane) {
this.tabPane = tabPane;
this.tabsMap = new HashMap<>();
initial();
}
private void initial() {
tabPane.getTabs().addListener(new ListChangeListener<Tab>() {
#Override
public void onChanged(Change<? extends Tab> c) {
while (c.next()) {
// if elements were added into list, the elements's text
// and the elements themselves need to be added into HashMap
if (c.wasAdded()) {
List<? extends Tab> addedTabs = c.getAddedSubList();
for (Tab tab : addedTabs) {
tabsMap.put(tab.getText(), tab);
}
}
// if elements were removed from list, the elements's text
// and the elements themselves need to be removed from HashMap
if(c.wasRemoved()){
List<? extends Tab> removedTabs = c.getRemoved();
for(Tab tab : removedTabs){
tabsMap.remove(tab.getText());
}
}
}
}
});
}
public boolean addTab(Tab tab) {
return this.tabPane.getTabs().add(tab);
}
public boolean addTabs(Tab... tabs) {
return this.tabPane.getTabs().addAll(tabs);
}
public boolean removeTab(String text){
return this.tabPane.getTabs().remove(getTabByText(text));
}
public Tab getTabByText(String text) {
return tabsMap.get(text);
}
}
```

JavaFX Disable TableColumn based on checkbox state

Am looking to disable a TableColumn<CustomObject, String> tableColumn based on a field value in the CustomObject only when the TableColumn<CustomObject, Boolean> tableColumnTwo checkbox is checked. I can disable the textbox inside public void updateItem(String s, boolean empty) however not sure how to check the state of checkbox inside updateItem
Below is the relevant code snippet, would highly appreciate if anyone can shed light on this
#FXML
private TableColumn<CustomObject, Boolean> tableColumnTwo;
#FXML
private TableColumn<CustomObject, String> tableColumn;
tableColumn.setCellFactory(
new Callback<TableColumn<CustomObject, String>, TableCell<CustomObject, String>>() {
#Override
public TableCell<CustomObject, String> call(TableColumn<CustomObject, String> paramTableColumn) {
return new TextFieldTableCell<CustomObject, String>(new DefaultStringConverter()) {
#Override
public void updateItem(String s, boolean empty) {
super.updateItem(s, empty);
TableRow<CustomObject> currentRow = getTableRow();
if(currentRow.getItem() != null && !empty) {
if (currentRow.getItem().getPetrified() == false) { // Need to check if checkbox is checked or not
setDisable(true);
setEditable(false);
this.setStyle("-fx-background-color: red");
} else {
setDisable(false);
setEditable(true);
setStyle("");
}
}
}
};
}
});
You can add a listener on the checkbox, which when checked will cause the table refresh.
data = FXCollections.observableArrayList(new Callback<CustomObject, Observable[]>() {
#Override
public Observable[] call(CustomObject param) {
return new Observable[]{param.petrifiedProperty()};
}
});
data.addListener(new ListChangeListener<CustomObject>() {
#Override
public void onChanged(ListChangeListener.Change<? extends CustomObject> c) {
while (c.next()) {
if (c.wasUpdated()) {
tableView.setItems(null);
tableView.layout();
tableView.setItems(FXCollections.observableList(data));
}
}
}
});
Your cellFactory would remain the same and would get called when a checkbox is checked/unchecked.
Usually, we expect cells being updated whenever they are notified about a change in the underlying data. To make certain that a notification is fired by the data on changing a property of an item, we need a list with an extractor on the properties that we are interested in, something like:
ObservableList<CustomObject> data = FXCollections.observableArrayList(
c -> new Observable[] {c.petrifiedProperty()}
);
With that in place the list fires a list change of type update whenever the pretified property changes.
Unfortunately, that's not enough due to a bug in fx: cells are not updated when receiving a listChange of type update from the underlying items. A dirty way around (read: don't use once the bug is fixed, it's using emergency api!) is to install a listener on the items and call table.refresh() when receiving an update.
An example:
import java.util.logging.Logger;
//import de.swingempire.fx.util.FXUtils;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.converter.DefaultStringConverter;
/**
* CheckBoxTableCell: update editable state of one column based of
* the boolean in another column
* https://stackoverflow.com/q/46290417/203657
*
* Bug in skins: cell not updated on listChange.wasUpdated
*
* reported as
* https://bugs.openjdk.java.net/browse/JDK-8187665
*/
#SuppressWarnings({ "rawtypes", "unchecked" })
public class TableViewUpdateBug extends Application {
/**
* TableCell that updates state based on another value in the row.
*/
public static class DisableTextFieldTableCel extends TextFieldTableCell {
public DisableTextFieldTableCel() {
super(new DefaultStringConverter());
}
/**
* Just to see whether or not this is called on update notification
* from the items (it's not)
*/
#Override
public void updateIndex(int index) {
super.updateIndex(index);
// LOG.info("called? " + index);
}
/**
* Implemented to change background based on
* visible property of row item.
*/
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
TableRow<TableColumn> currentRow = getTableRow();
boolean editable = false;
if (!empty && currentRow != null) {
TableColumn column = currentRow.getItem();
if (column != null) {
editable = column.isVisible();
}
}
if (!empty) {
setDisable(!editable);
setEditable(editable);
if (editable) {
this.setStyle("-fx-background-color: red");
} else {
this.setStyle("-fx-background-color: green");
}
} else {
setStyle("-fx-background-color: null");
}
}
}
#Override
public void start(Stage primaryStage) {
// data: list of tableColumns with extractor on visible property
ObservableList<TableColumn> data = FXCollections.observableArrayList(
c -> new Observable[] {c.visibleProperty()});
data.addAll(new TableColumn("first"), new TableColumn("second"));
TableView<TableColumn> table = new TableView<>(data);
table.setEditable(true);
// hack-around: call refresh
data.addListener((ListChangeListener) c -> {
boolean wasUpdated = false;
boolean otherChange = false;
while(c.next()) {
if (c.wasUpdated()) {
wasUpdated = true;
} else {
otherChange = true;
}
}
if (wasUpdated && !otherChange) {
table.refresh();
}
//FXUtils.prettyPrint(c);
});
TableColumn<TableColumn, String> text = new TableColumn<>("Text");
text.setCellFactory(c -> new DisableTextFieldTableCel());
text.setCellValueFactory(new PropertyValueFactory<>("text"));
TableColumn<TableColumn, Boolean> visible = new TableColumn<>("Visible");
visible.setCellValueFactory(new PropertyValueFactory<>("visible"));
visible.setCellFactory(CheckBoxTableCell.forTableColumn(visible));
table.getColumns().addAll(text, visible);
BorderPane root = new BorderPane(table);
Scene scene = new Scene(root, 300, 150);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TableViewUpdateBug.class.getName());
}

Is there a way of tracking stages

I want to track focused node to perform some action after user inactivity on that node. But focused for my application means that there could be only 1 focus node in the entire application (application has numerous Stages and they creation are out of my control. I also cannot force different department of my company to use registry to register stages after they create them).
E.g. I have a property for focused component scene.focusOwnerProperty(), I can track whether Stage is stage.focusedProperty() but I'm searching the way to retrieve all stages.
Do you know the way?
I found a solution. com.sun.javafx.stage.StageHelper.getStages() list all stages.
You can Use Window[] windows = Window.getWindows(); this is part of the Public Api and works fine for me.
Thanks guys for answers. I write a class which provides an easy way to track currently focused Node and "focused" Scene. "Focused" means the focused component from focused Stage. If there are any mistakes in my code I would be grateful for if you notice and inform me.
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Stage;
import com.sun.javafx.stage.StageHelper;
/**
* Tracks focused component among all {#link Stage}'s. Currently this is singleton but it will make service
* out of it.
*
* #author created: kszymanek on 8 sty 2016 15:19:32
*/
public class GlobalFocusTracker
{
private static final GlobalFocusTracker INSTANCE = new GlobalFocusTracker();
/**
* tracks stage list and each stage and registers {#link #sceneListener} on
* the focused stage
*/
private StagesListener stagesListener;
/**
* tracks scene of the focused stage and registers
* {#link #focusOwnerListener} on it
*/
private SceneListener sceneListener;
private FocusOwnerListener focusOwnerListener;
private ReadOnlyObjectWrapper focusedNodeProperty = new ReadOnlyObjectWrapper();
private ReadOnlyObjectWrapper focusedSceneProperty = new ReadOnlyObjectWrapper();
private GlobalFocusTracker()
{
}
public static GlobalFocusTracker getInstance()
{
return INSTANCE;
}
/**
* #return current {#link Scene} from the currently focused {#link Stage}
*/
public ReadOnlyObjectProperty focusedSceneProperty()
{
init();
return focusedSceneProperty.getReadOnlyProperty();
}
/**
* #return focused node among all stages. There could be one focus owner at
* the time. If end user focuses some other application there would
* be no focus node.
*/
public ReadOnlyObjectProperty focusedNodeProperty()
{
init();
return focusedNodeProperty.getReadOnlyProperty();
}
private void init()
{
if(stagesListener == null)
{
stagesListener = new StagesListener();
sceneListener = new SceneListener();
focusOwnerListener = new FocusOwnerListener();
stagesListener.register();
}
}
private class StagesListener implements ListChangeListener, ChangeListener
{
private ObservableList stages;
#Override
public void onChanged(javafx.collections.ListChangeListener.Change aChange)
{
while(aChange.next())
{
if(aChange.wasRemoved())
{
for(Stage stage : aChange.getRemoved())
{
stage.focusedProperty().removeListener(this);
}
}
if(aChange.wasAdded())
{
for(Stage stage : aChange.getAddedSubList())
{
stage.focusedProperty().addListener(this);
}
}
}
}
#Override
public void changed(ObservableValue aObservable, Boolean aOld, Boolean aNew)
{
Stage focusedStage = null;
for(Stage stage : stages)
{
if(stage.isFocused())
{
focusedStage = stage;
}
}
sceneListener.register(focusedStage);
}
public void register()
{
if(stages == null)
{
stages = StageHelper.getStages();
stages.addListener(this);
for(Stage stage : stages)
{
stage.focusedProperty().addListener(this);
if(stage.isFocused())
{
sceneListener.register(stage);
}
}
}
}
}
private class SceneListener implements ChangeListener
{
private Stage stage;
#Override
public void changed(ObservableValue aObservable, Scene aOld, Scene aNew)
{
focusOwnerListener.register(aNew);
}
/**
* #param aStage is {#code null} protected
*/
public void register(Stage aStage)
{
if(aStage != stage)
{
unregister();
stage = aStage;
if(aStage != null)
{
aStage.sceneProperty().addListener(this);
focusOwnerListener.register(aStage.getScene());
}
}
}
public void unregister()
{
if(stage != null)
{
focusOwnerListener.unregister();
stage.sceneProperty().removeListener(this);
stage = null;
}
}
}
private class FocusOwnerListener implements ChangeListener
{
private Scene scene;
#Override
public void changed(ObservableValue aObservable, Node aOld, Node aNew)
{
focusedNodeProperty.set(aNew);
}
/**
* #param aScene can be {#code null} in such case it is only an equivalent
* of {#link #unregister()}
*/
public void register(Scene aScene)
{
if(scene != aScene)
{
unregister();
scene = aScene;
focusedSceneProperty.set(aScene);
if(aScene != null)
{
focusedNodeProperty.set(aScene.getFocusOwner());
aScene.focusOwnerProperty().addListener(this);
}
}
}
public void unregister()
{
if(scene != null)
{
focusedSceneProperty.set(null);
focusedNodeProperty.set(null);
scene.focusOwnerProperty().removeListener(this);
scene = null;
}
}
}
}

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();
}
}
}

Resources