import java.util.ArrayList; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; import javafx.application.Application; import javafx.application.Platform; import javafx.beans.property.SimpleStringProperty; 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.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.CheckMenuItem; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.SelectionMode; import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn.CellDataFeatures; import javafx.scene.control.TablePosition; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.util.Callback; public class TableViewApp extends Application { public static final String CONTENT_PANE_ID = "content_pane_id"; public static final String APPLY_SELECTION_BTN_ID = "apply_selection_btn_id"; public static final String CLEAR_SELECTION_BTN_ID = "clear_selection_btn_id"; public static final String REMOVE_SELECTED_BTN_ID = "remove_selected"; public static final String REFILL_BNT_ID = "refill_table"; public static final String FILL_LONG_BNT_ID = "Fill by long items"; public static final String TEXT_TO_ADD_ID = "text_to_add"; public static final String SINGLE_CELL_SELECTION_ID = "single_cell_selection"; public static final String ROWS_NUMBER_ID = "rows_number_id"; public static final String COLUMNS_NUMBER_ID = "columns_number_id"; public static final String REMOVE_COLUMN_ID = "remove_column_id"; Pane content; TableView view; ObservableList data = FXCollections.observableArrayList(); CheckBox multiSelectionCheck; CheckBox singleCellSelectionCheck; public static void main(String args[]) { launch(args); } protected Scene getScene() { return new TableViewScene(); } @Override public void start(Stage stage) throws Exception { stage.setTitle("TableView scrolling to column index"); Scene scene = getScene(); // System.setProperty("http.proxyHost", "www-proxy.uk.oracle.com"); // System.setProperty("http.proxyPort", "80"); // scene.getStylesheets().add(TreeTableApp.class.getResource("font.css").toExternalForm()); stage.setScene(scene); stage.show(); } public class TableViewScene extends Scene { public TableViewScene() { super(new HBox(5)); multiSelectionCheck = new CheckBox("multi-selection"); singleCellSelectionCheck = new CheckBox("single cell selection"); Pane cont = (Pane) getRoot(); cont.getChildren().add(getTestContainer()); cont.getChildren().add(getControlsContainer()); } } /** * A container containing the tested UI. * * @return */ protected Node getTestContainer() { content = new Pane(); content.setId(CONTENT_PANE_ID); content.setMinSize(500, 500); content.setMaxSize(500, 500); content.setPrefSize(500, 500); view = new TableView(); view.setItems(data); view.setMinSize(400, 400); view.setMaxSize(400, 400); view.setPrefSize(400, 400); reset(DATA_ITEMS_NUM, DATA_FIELDS_NUM, 0); content.getChildren().add(view); view.setTranslateX(50); view.setTranslateY(50); return content; } protected void reset(int rows, int columns, int length) { fill(rows, columns, length); view.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); view.getSelectionModel().setCellSelectionEnabled(false); multiSelectionCheck.setSelected(false); singleCellSelectionCheck.setSelected(false); } protected void refill(int rows, int columns, int length) { data = FXCollections.observableArrayList(); reset(rows, columns, length); view.setItems(data); } protected void fill(int rowsNum, int columnsNum, int length) { data.clear(); for (int i = 0; i < rowsNum; i++) { data.add(new Data(String.format("%02d", i), columnsNum, length)); } view.getColumns().clear(); final ContextMenu contextMenu = new ContextMenu(); for (int i = 0; i < columnsNum; i++) { view.getColumns().add(createColumn(i, contextMenu)); } view.setContextMenu(contextMenu); } protected TableColumn createColumn(final int index, final ContextMenu contextMenu) { final TableColumn field = new TableColumn("field " + index); final CheckMenuItem visible = new CheckMenuItem("field " + index); visible.setSelected(field.isVisible()); visible.selectedProperty().addListener(new ChangeListener() { public void changed(ObservableValue ov, Boolean t, Boolean t1) { field.setVisible(t1); } }); contextMenu.getItems().add(visible); final ContextMenu columnContextMenu = new ContextMenu(); field.setContextMenu(columnContextMenu); CheckMenuItem sortable = new CheckMenuItem("Sortable"); sortable.setSelected(field.isSortable()); sortable.setId(REMOVE_COLUMN_ID); sortable.selectedProperty().addListener(new ChangeListener() { public void changed(ObservableValue ov, Boolean t, Boolean t1) { field.setSortable(t1); } }); columnContextMenu.getItems().add(sortable); final MenuItem removeColumn = new MenuItem("Remove column"); removeColumn.setId(REMOVE_COLUMN_ID); removeColumn.setOnAction(new EventHandler() { public void handle(ActionEvent t) { view.getColumns().remove(field); contextMenu.getItems().remove(visible); } }); columnContextMenu.getItems().add(removeColumn); field.setCellValueFactory(new Callback, ObservableValue>() { public ObservableValue call(CellDataFeatures p) { return ((Data) p.getValue()).data.get(index); } }); return field; } /** * A container that contains all the controls which are used for tested UI * customization * * @return */ protected Node getControlsContainer() { VBox res = new VBox(10); VBox resetVBox = new VBox(); res.getChildren().add(resetVBox); Button reset = new Button("Reset"); resetVBox.getChildren().add(reset); Button refill = new Button("Refill"); refill.setId(REFILL_BNT_ID); resetVBox.getChildren().add(refill); resetVBox.getChildren().add(new Label("rows number")); final TextField rowsNumberTf = new TextField(String.valueOf(DATA_ITEMS_NUM)); rowsNumberTf.setId(ROWS_NUMBER_ID); resetVBox.getChildren().add(rowsNumberTf); resetVBox.getChildren().add(new Label("columns number")); final TextField columnsNumberTf = new TextField(String.valueOf(DATA_FIELDS_NUM)); rowsNumberTf.setId(COLUMNS_NUMBER_ID); resetVBox.getChildren().add(columnsNumberTf); reset.setOnAction(new EventHandler() { public void handle(ActionEvent t) { reset(Integer.valueOf(rowsNumberTf.getText()), Integer.valueOf(columnsNumberTf.getText()), 0); } }); refill.setOnAction(new EventHandler() { public void handle(ActionEvent t) { refill(Integer.valueOf(rowsNumberTf.getText()), Integer.valueOf(columnsNumberTf.getText()), 0); } }); VBox insertItemVBox = new VBox(); final TextField itemToInsert = new TextField("new item"); itemToInsert.setId(TEXT_TO_ADD_ID); final TextField index = new TextField("0"); Button insertItemBtn = new Button("Insert item"); insertItemBtn.setOnAction(new EventHandler() { public void handle(ActionEvent t) { data.add(Integer.parseInt(index.getText()), new Data(itemToInsert.getText(), DATA_FIELDS_NUM, 0)); } }); insertItemVBox.getChildren().add(insertItemBtn); insertItemVBox.getChildren().add(itemToInsert); insertItemVBox.getChildren().add(new Label(" at index ")); insertItemVBox.getChildren().add(index); res.getChildren().add(insertItemVBox); HBox removeItemHbox = new HBox(); final TextField removeItemIndex = new TextField("0"); Button removeItemButton = new Button("Remove item"); removeItemButton.setOnAction(new EventHandler() { public void handle(ActionEvent t) { data.remove(Integer.parseInt(removeItemIndex.getText())); } }); removeItemHbox.getChildren().add(removeItemButton); removeItemHbox.getChildren().add(removeItemIndex); res.getChildren().add(removeItemHbox); HBox insertColumnBox = new HBox(); final TextField columnIndexTf = new TextField("0"); Button insertColumnBtn = new Button("Insert column"); insertColumnBtn.setOnAction(new EventHandler() { public void handle(ActionEvent t) { int index = Integer.parseInt(columnIndexTf.getText()); view.getColumns().add(index, createColumn(index, view.getContextMenu())); } }); insertColumnBox.getChildren().add(insertColumnBtn); insertColumnBox.getChildren().add(columnIndexTf); res.getChildren().add(insertColumnBox); HBox removeColumn = new HBox(); final TextField removeColumnIndex = new TextField("0"); Button removeColumnButton = new Button("Remove column"); removeColumnButton.setOnAction(new EventHandler() { public void handle(ActionEvent t) { view.getColumns().remove(Integer.parseInt(removeColumnIndex.getText())); } }); removeColumn.getChildren().add(removeColumnButton); removeColumn.getChildren().add(removeColumnIndex); res.getChildren().add(removeColumn); Button removeSelectedButton = new Button("Remove selected"); removeSelectedButton.setId(REMOVE_SELECTED_BTN_ID); removeSelectedButton.setOnAction(new EventHandler() { public void handle(ActionEvent t) { ObservableList observableList = view.getSelectionModel().getSelectedItems(); ArrayList list = new ArrayList(); for (Data item : observableList) { list.add(item); // ObservableList.toArray() is not implemented } for (Data item : list) { data.remove(item); } } }); res.getChildren().add(removeSelectedButton); HBox multipleSelectionHBox = new HBox(); res.getChildren().add(multipleSelectionHBox); multiSelectionCheck.selectedProperty().addListener(new ChangeListener() { public void changed(ObservableValue ov, Boolean t, Boolean t1) { view.getSelectionModel().setSelectionMode(t1 ? SelectionMode.MULTIPLE : SelectionMode.SINGLE); } }); multipleSelectionHBox.getChildren().add(multiSelectionCheck); singleCellSelectionCheck.setId(SINGLE_CELL_SELECTION_ID); singleCellSelectionCheck.selectedProperty().addListener(new ChangeListener() { public void changed(ObservableValue ov, Boolean t, Boolean t1) { view.getSelectionModel().setCellSelectionEnabled(t1); } }); multipleSelectionHBox.getChildren().add(singleCellSelectionCheck); VBox selectionVBox = new VBox(); selectionVBox.getChildren().add(new Label("Selection: ")); final TextField selection = new TextField(""); selectionVBox.getChildren().add(selection); view.getSelectionModel().getSelectedItems().addListener(new ListChangeListener() { public void onChanged(Change change) { String res = ""; for (Object obj : view.getSelectionModel().getSelectedCells()) { TablePosition tablePos = (TablePosition) obj; if (!res.isEmpty()) { res += ", "; } if (view.getSelectionModel().isCellSelectionEnabled()) { res += tablePos.getTableColumn().getCellData(tablePos.getRow()); } else { res += tablePos.getRow(); } } selection.setText(res); } }); Button applySelectionBtn = new Button("Apply"); applySelectionBtn.setId(APPLY_SELECTION_BTN_ID); applySelectionBtn.setOnAction(new EventHandler() { public void handle(ActionEvent t) { StringTokenizer indices = new StringTokenizer(selection.getText(), ","); while (indices.hasMoreTokens()) { view.getSelectionModel().select(Integer.parseInt(indices.nextToken())); } } }); selectionVBox.getChildren().add(applySelectionBtn); Button clearSelection = new Button("Clear"); clearSelection.setId(CLEAR_SELECTION_BTN_ID); clearSelection.setOnAction(new EventHandler() { public void handle(ActionEvent t) { view.getSelectionModel().clearSelection(); } }); selectionVBox.getChildren().add(clearSelection); res.getChildren().add(selectionVBox); HBox fillHBox = new HBox(5); res.getChildren().add(fillHBox); Button fillLongBtn = new Button("Fill by long items"); fillLongBtn.setId(FILL_LONG_BNT_ID); fillLongBtn.setOnAction(new EventHandler() { public void handle(ActionEvent t) { reset(DATA_ITEMS_NUM, DATA_FIELDS_NUM, 100); } }); fillHBox.getChildren().add(fillLongBtn); final TextField columnIndexToScroll = new TextField(); columnIndexToScroll.setPromptText("Index to scroll"); Button scrollTo = new Button("Scroll to"); scrollTo.setOnAction(new EventHandler() { public void handle(ActionEvent t) { int idx; try { idx = Integer.valueOf(columnIndexToScroll.getText()); } catch (NumberFormatException nex) { System.err.println(nex.getMessage()); return; } //Let index be negative in case we need it for testing System.out.println("idx = " + idx); view.scrollToColumnIndex(idx); } }); HBox scrollingControls = new HBox(3, columnIndexToScroll, scrollTo); res.getChildren().add(scrollingControls); final TextField columnIndexToResize = new TextField(); columnIndexToResize.setPromptText("column index for resizing"); final TextField columnWidth = new TextField(); columnWidth.setPromptText("delta"); Button resizeColumn = new Button("Resize column"); resizeColumn.setOnAction(new EventHandler() { @Override public void handle(ActionEvent actionEvent) { final int index = Integer.valueOf(columnIndexToResize.getText()); final double width = Double.valueOf(columnWidth.getText()); // view.resizeColumn(view.getColumns().get(index), width); new Thread(new Runnable() { @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } System.err.println("Sleep finished. Resizing started."); Platform.runLater(new Runnable() { @Override public void run() { view.resizeColumn(view.getColumns().get(index), width); } }); } }).start(); } }); HBox resizingControls = new HBox(3, columnIndexToResize, columnWidth, resizeColumn); res.getChildren().add(resizingControls); return res; } protected static String createItem(int level, int item, int length) { StringBuilder builder = new StringBuilder(); builder.append("Level " + level + " Item " + item); if (length > 0) { builder.append(" l"); for (int j = 0; j < length; j++) { builder.append("o"); } builder.append("ng"); } return builder.toString(); } public static class Data implements Comparable { protected String name; protected ArrayList data = new ArrayList(); public Data(String name, int fieldsNum, int length) { this.name = name; for (int i = 0; i < fieldsNum; i++) { data.add(new SimpleStringProperty("item " + name + " field " + i + getLongItem(length))); } } protected String getLongItem(int length) { if (length == 0) { return ""; } String str = " l"; for (int i = 0; i < length; i++) { str += "o"; } return str + "ng"; } public int compareTo(Data other) { if (this == other) { return 0; } else { return this.name.compareTo(other.name); } } @Override public String toString() { return name; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (null == other || !Data.class.isAssignableFrom(other.getClass())) { return false; } return this.name.equals(((Data) other).name); } @Override public int hashCode() { int hash = 3; hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0); return hash; } } static final int DATA_FIELDS_NUM = 5; static final int DATA_ITEMS_NUM = 17; }