如何在TableView中复制/粘贴表格单元格

时间:2015-04-19 07:54:42

标签: javafx tableview

问题

处理表时最基本的需求之一是复制/粘贴表格单元格的数据。 JavaFX TableView不支持开箱即用。

问题

如何访问表格单元格而不是数据对象,以便将剪贴板数据粘贴到选定的单元格中?

代码

这是我到目前为止所得到的:

TableUtils.java

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;

public class TableUtils {

    /**
     * Install the keyboard handler:
     *   + CTRL + C = copy to clipboard
     *   + CTRL + V = paste to clipboard
     * @param table
     */
    public static void installCopyPasteHandler(TableView<?> table) {

        // install copy/paste keyboard handler
        table.setOnKeyPressed(new TableKeyEventHandler());

    }

    /**
     * Copy/Paste keyboard event handler.
     * The handler uses the keyEvent's source for the clipboard data. The source must be of type TableView.
     */
    public static class TableKeyEventHandler implements EventHandler<KeyEvent> {

        KeyCodeCombination copyKeyCodeCompination = new KeyCodeCombination(KeyCode.C, KeyCombination.CONTROL_ANY);
        KeyCodeCombination pasteKeyCodeCompination = new KeyCodeCombination(KeyCode.V, KeyCombination.CONTROL_ANY);

        public void handle(final KeyEvent keyEvent) {

            if (copyKeyCodeCompination.match(keyEvent)) {

                if( keyEvent.getSource() instanceof TableView) {

                    // copy to clipboard
                    copySelectionToClipboard( (TableView<?>) keyEvent.getSource());

                    // event is handled, consume it
                    keyEvent.consume();

                }

            } 
            else if (pasteKeyCodeCompination.match(keyEvent)) {

                if( keyEvent.getSource() instanceof TableView) {

                    // copy to clipboard
                    pasteClipboard( (TableView<?>) keyEvent.getSource());

                    // event is handled, consume it
                    keyEvent.consume();

                }

            } 

        }

    }

    /**
     * Get table selection and copy it to the clipboard.
     * @param table
     */
    public static void copySelectionToClipboard(TableView<?> table) {

        StringBuilder clipboardString = new StringBuilder();

        ObservableList<TablePosition> positionList = table.getSelectionModel().getSelectedCells();

        int prevRow = -1;

        for (TablePosition position : positionList) {

            int row = position.getRow();
            int col = position.getColumn();

            Object cell = (Object) table.getColumns().get(col).getCellData(row);

            // null-check: provide empty string for nulls
            if (cell == null) {
                cell = "";
            }

            // determine whether we advance in a row (tab) or a column
            // (newline).
            if (prevRow == row) {

                clipboardString.append('\t');

            } else if (prevRow != -1) {

                clipboardString.append('\n');

            }

            // create string from cell
            String text = cell.toString();

            // add new item to clipboard
            clipboardString.append(text);

            // remember previous
            prevRow = row;
        }

        // create clipboard content
        final ClipboardContent clipboardContent = new ClipboardContent();
        clipboardContent.putString(clipboardString.toString());

        // set clipboard content
        Clipboard.getSystemClipboard().setContent(clipboardContent);

        System.out.println( "Clipboard string: " + clipboardContent);

    }

    public static void pasteClipboard(TableView<?> table) {

        TablePosition focusedCellPosition = table.getFocusModel().getFocusedCell();

        System.out.println("Pasting into cells starting at " + focusedCellPosition);

        String pasteString = Clipboard.getSystemClipboard().getString();

        System.out.println(pasteString);

        Pattern pattern = Pattern.compile("([^\t]*)\t([^\t]*)\t([^\n]*)(\n)?");
        Matcher matcher = pattern.matcher(pasteString);
        while (matcher.find()) {

            System.out.println(matcher.group(1) + "," + matcher.group(2) + "," + matcher.group(3));

            // TODO: what now? how to paste the data?  

        }

    }


}

TableCopyCellsDemo.java

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class TableCopyCellsDemo extends Application {

    private final ObservableList<Person> data = FXCollections.observableArrayList(new Person("Jacob", "Smith", 18), new Person("Isabella", "Johnson", 19), new Person("Ethan", "Williams", 20), new Person("Michael", "Brown", 21));

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {

        stage.setWidth(500);
        stage.setHeight(550);

        // create table columns
        TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));

        TableColumn<Person, String> lastNameCol = new TableColumn<Person, String>("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));

        TableColumn<Person, Integer> ageCol = new TableColumn<Person, Integer>("Age");
        ageCol.setMinWidth(60);
        ageCol.setCellValueFactory(new PropertyValueFactory<Person, Integer>("age"));


        TableView<Person> table = new TableView<>();
        table.setPlaceholder(new Text("No content in table"));
        table.setItems(data);
        table.getColumns().addAll(firstNameCol, lastNameCol, ageCol);

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 10, 10, 10));

        BorderPane borderPane = new BorderPane();
        borderPane.setCenter(table);

        vbox.getChildren().addAll(borderPane);

        vbox.getChildren().add( new Label( "Select cells and press CTRL+C. Paste the data into Excel or Notepad"));

        Scene scene = new Scene(vbox);

        stage.setScene(scene);
        stage.show();

        // enable multi-selection
        table.getSelectionModel().setCellSelectionEnabled(true);
        table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

        // enable copy/paste
        TableUtils.installCopyPasteHandler(table);
    }


    public static class Person {

        private final StringProperty firstName;
        private final StringProperty lastName;
        private final IntegerProperty age;

        private Person(String fName, String lName, Integer age) {
            this.firstName = new SimpleStringProperty(fName);
            this.lastName = new SimpleStringProperty(lName);
            this.age = new SimpleIntegerProperty(age);
        }

        public final StringProperty firstNameProperty() {
            return this.firstName;
        }

        public final java.lang.String getFirstName() {
            return this.firstNameProperty().get();
        }

        public final void setFirstName(final java.lang.String firstName) {
            this.firstNameProperty().set(firstName);
        }

        public final StringProperty lastNameProperty() {
            return this.lastName;
        }

        public final java.lang.String getLastName() {
            return this.lastNameProperty().get();
        }

        public final void setLastName(final java.lang.String lastName) {
            this.lastNameProperty().set(lastName);
        }

        public final IntegerProperty ageProperty() {
            return this.age;
        }

        public final int getAge() {
            return this.ageProperty().get();
        }

        public final void setAge(final int age) {
            this.ageProperty().set(age);
        }
    }
}

问题是pasteClipboard方法中的 TODO 。您可以通过 CTRL + C 将所选单元格的数据复制到剪贴板。使用 CTRL + V ,您可以从剪贴板获取数据并进行分析。但我还没有找到将数据直接写入表格的手段。

非常感谢!

1 个答案:

答案 0 :(得分:4)

经过一些挖掘和阅读有关反射的解决方案后,我发现自己比我想象的要容易。最重要的是:没有反思。

我以为我会分享这些代码,希望它对别人有帮助。

这是修改单元格的基本方法。这正是我发现的。如果有人知道更好的方法,请分享。

        // get cell
        TableColumn tableColumn = table.getColumns().get(colIndex);
        ObservableValue observableValue = tableColumn.getCellObservableValue(rowIndex);

        if( observableValue instanceof StringProperty) { 

            ((StringProperty) observableValue).set(clipboardCellContent);

        }

因此粘贴方法基本上如下所示:

public static void pasteClipboard( TableView<?> table) {

    // abort if there's not cell selected to start with
    if( table.getSelectionModel().getSelectedCells().size() == 0) {
        return;
    }

    // get the cell position to start with
    TablePosition pasteCellPosition = table.getSelectionModel().getSelectedCells().get(0);

    System.out.println("Pasting into cell " + pasteCellPosition);

    String pasteString = Clipboard.getSystemClipboard().getString();

    System.out.println(pasteString);

    int rowClipboard = -1;

    StringTokenizer rowTokenizer = new StringTokenizer( pasteString, "\n");
    while( rowTokenizer.hasMoreTokens()) {

        rowClipboard++;

        String rowString = rowTokenizer.nextToken();

        StringTokenizer columnTokenizer = new StringTokenizer( rowString, "\t");

        int colClipboard = -1;

        while( columnTokenizer.hasMoreTokens()) {

            colClipboard++;

            // calculate the position in the table cell
            int rowTable = pasteCellPosition.getRow() + rowClipboard;
            int colTable = pasteCellPosition.getColumn() + colClipboard;

            // skip if we reached the end of the table
            if( rowTable >= table.getItems().size()) {
                continue;
            }
            if( colTable >= table.getColumns().size()) {
                continue;
            }

            String clipboardCellContent = columnTokenizer.nextToken();

            // System.out.println( rowClipboard + "/" + colClipboard + ": " + cell);

            // get cell
            TableColumn tableColumn = table.getColumns().get(colTable);
            ObservableValue observableValue = tableColumn.getCellObservableValue(rowTable);

            System.out.println( rowTable + "/" + colTable + ": " +observableValue);

            // TODO: handle double, etc
            if( observableValue instanceof StringProperty) { 

                ((StringProperty) observableValue).set(clipboardCellContent);

            }
            else if( observableValue instanceof IntegerProperty) { 

                int value;
                try {
                    value = NumberFormat.getInstance().parse(clipboardCellContent).intValue();
                    ((IntegerProperty) observableValue).set(value);
                } catch (ParseException e) {
                    e.printStackTrace();
                }

            }
        }

    }

}

您可以在this gist上获取完整的代码。