1

我正在尝试修改UITableView - Better Editing through Binding 中概述的解决方案?(感谢 jKaufmann 提供的出色示例)以允许向表中添加行。

我介绍了一个按钮,当单击该按钮时,将调用代码以向支持该表的 ObservableList 添加一个额外的 TableData 行。

新行显示没有事故。但是,在添加了几行并使用向上/向下键滚动表格几次之后,表格中的随机行开始被空白行替换。

通常需要添加几行,选择一行,向下遍历列表(使用键盘)到新行,添加更多行,向下遍历到新行并一直到顶部再次重现问题。要选择表格中的一行,我单击一行的边缘,以便选择该行而不是单个单元格。

源代码(本质上是 jkaufmann 的示例修改为包含“添加行”按钮)在这里。

package tablevieweditingwithbinding;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TableViewEditingExample2 extends Application {

    public static class TableData {

        private SimpleStringProperty firstName, lastName, phone, email;
        private ObjectProperty<SimpleStringProperty> firstNameObject;

        public TableData(String firstName, String lastName, String phone, String email) {
            this.firstName = new SimpleStringProperty(firstName);
            this.firstNameObject = new SimpleObjectProperty(firstNameObject);
            this.lastName = new SimpleStringProperty(lastName);
            this.phone = new SimpleStringProperty(phone);
            this.email = new SimpleStringProperty(email);
        }

        public String getEmail() {
            return email.get();
        }

        public void setEmail(String email) {
            this.email.set(email);
        }

        public SimpleStringProperty emailProperty() {
            return email;
        }

        public String getFirstName() {
            return firstName.get();
        }

        public SimpleStringProperty getFirstNameObject() {
            return firstNameObject.get();
        }

        public void setFirstNameObject(SimpleStringProperty firstNameObject) {
            this.firstNameObject.set(firstNameObject);
        }

        public ObjectProperty<SimpleStringProperty> firstNameObjectProperty() {
            return firstNameObject;
        }

        public void setFirstName(String firstName) {
            this.firstName.set(firstName);
        }

        public SimpleStringProperty firstNameProperty() {
            return firstName;
        }

        public String getLastName() {
            return lastName.get();
        }

        public void setLastName(String lastName) {
            this.lastName.set(lastName);
        }

        public SimpleStringProperty lastNameProperty() {
            return lastName;
        }

        public String getPhone() {
            return phone.get();
        }

        public void setPhone(String phone) {
            this.phone.set(phone);
        }

        public SimpleStringProperty phoneProperty() {
            return phone;
        }
    }

    public static class TextFieldCellFactory
            implements Callback<TableColumn<TableData, String>, TableCell<TableData, String>> {

        @Override
        public TableCell<TableData, String> call(TableColumn<TableData, String> param) {
            TextFieldCell textFieldCell = new TextFieldCell();
            return textFieldCell;
        }

        public static class TextFieldCell extends TableCell<TableData, String> {

            private TextField textField;
            private StringProperty boundToCurrently = null;

            public TextFieldCell() {
                String strCss;
                // Padding in Text field cell is not wanted - we want the Textfield itself to "be"
                // The cell.  Though, this is aesthetic only.  to each his own.  comment out
                // to revert back.  
                strCss = "-fx-padding: 0;";
                textField = new TextField();

                // 
                // Default style pulled from caspian.css. Used to play around with the inset background colors
                // ---trying to produce a text box without borders
                strCss = ""
                        + //"-fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;" +
                        "-fx-background-color: transparent;"
                        + //"-fx-background-insets: 0, 1, 2;" +
                        //"-fx-background-insets: 0;" +
                        //"-fx-background-radius: 3, 2, 2;" +
                        //"-fx-background-radius: 0;" +
                        //"-fx-padding: 3 5 3 5;" +   /*Play with this value to center the text depending on cell height??*/
                        //"-fx-padding: 0 0 0 0;" +
                        //"-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);" +
                        //"-fx-accent: derive(-fx-control-inner-background, -40%);" +
                        "-fx-cell-hover-color: derive(-fx-control-inner-background, -20%);"
                        + "-fx-cursor: text;"
                        + "";

                // 
                textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
                    public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                        TextField tf = (TextField) getGraphic();
                        String strStyleGotFocus = "-fx-background-color: purple, -fx-text-box-border, -fx-control-inner-background;"
                                + "-fx-background-insets: -0.4, 1, 2;"
                                + "-fx-background-radius: 3.4, 2, 2;";
                        String strStyleLostFocus = //"-fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;" +
                                "-fx-background-color: transparent;"
                                + //"-fx-background-insets: 0, 1, 2;" +
                                "-fx-background-insets: 0;"
                                + //"-fx-background-radius: 3, 2, 2;" +
                                "-fx-background-radius: 0;"
                                + "-fx-padding: 3 5 3 5;"
                                + /**/ //"-fx-background-fill: green;" +   /**/
                                //"-fx-background-color: green;" +
                                "-fx-background-opacity: 0;"
                                + //"-fx-opacity: 0;" +
                                //"-fx-padding: 0 0 0 0;" +
                                "-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);"
                                + "-fx-cursor: text;"
                                + "";

                        if (newValue.booleanValue()) {
                            tf.setStyle(strStyleGotFocus);
                        } else {
                            tf.setStyle(strStyleLostFocus);
                        }

                    }
                });
                textField.setStyle(strCss);
                this.setGraphic(textField);
            }

            @Override
            protected void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);
                if (!empty) {
                    // Show the Text Field
                    this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

                    // Retrieve the actual String Property that should be bound to the TextField
                    // If the TextField is currently bound to a different StringProperty
                    // Unbind the old property and rebind to the new one
                    ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex());
                    SimpleStringProperty sp = (SimpleStringProperty) ov;

                    if (this.boundToCurrently == null) {
                        this.boundToCurrently = sp;
                        this.textField.textProperty().bindBidirectional(sp);
                    } else {
                        if (this.boundToCurrently != sp) {
                            this.textField.textProperty().unbindBidirectional(this.boundToCurrently);
                            this.boundToCurrently = sp;
                            this.textField.textProperty().bindBidirectional(this.boundToCurrently);
                        }
                    }
                    System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue());
                } else {
                    this.setContentDisplay(ContentDisplay.TEXT_ONLY);
                }
            }
        }
    }
    private final TableView<TableData> table = new TableView<TableData>();
    final ObservableList<TableData> ol =
            FXCollections.observableArrayList(
            new TableData("Wilma", "Flintstone", "555-123-4567", "WFlintstone@gmail.com"),
            new TableData("Fred", "Flintstone", "555-123-4567", "FFlintstone@gmail.com"),
            new TableData("Barney", "Flintstone", "555-123-4567", "Barney@gmail.com"),
            new TableData("Bugs", "Bunny", "555-123-4567", "BugsB@gmail.com"),
            new TableData("Yo", "Sam", "555-123-4567", "ysam@gmail.com"),
            new TableData("Tom", "", "555-123-4567", "tom@gmail.com"),
            new TableData("Jerry", "", "555-123-4567", "Jerry@gmail.com"),
            new TableData("Peter", "Pan", "555-123-4567", "Ppan@gmail.com"),
            new TableData("Daffy", "Duck", "555-123-4567", "dduck@gmail.com"),
            new TableData("Tazmanian", "Devil", "555-123-4567", "tdevil@gmail.com"),
            new TableData("Mickey", "Mouse", "555-123-4567", "mmouse@gmail.com"),
            new TableData("Mighty", "Mouse", "555-123-4567", "mimouse@gmail.com"));

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Application.launch(args);
    }
    static int counter = 1;

    @Override
    public void start(Stage Stage) {
        Stage.setTitle("Editable Table");
        BorderPane borderPane = new BorderPane();
        Scene scene = new Scene(borderPane, 800, 600);

        // top of border pane
        Button b1 = new Button("Change value in table list");
        Button b2 = new Button("Add row");
        HBox hbox = new HBox(10);
        hbox.setStyle("-fx-background-color: #336699");
        hbox.setAlignment(Pos.BOTTOM_CENTER);
        HBox.setMargin(b2, new Insets(10, 0, 10, 0));
        HBox.setMargin(b1, new Insets(10, 0, 10, 0));
        hbox.getChildren().addAll(b1, b2);
        borderPane.setTop(hbox);
        BorderPane.setAlignment(hbox, Pos.CENTER);

        // Button Events
        b1.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                String curFirstName = ol.get(0).getFirstName();
                if (curFirstName.contentEquals("Jason")) {
                    ol.get(0).setFirstName("Paul");
                } else {
                    ol.get(0).setFirstName("Jason");
                }
            }
        });

        b2.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        int dataListSize = 0;
                        dataListSize = ol.size();
                        System.out.println("Table size = " + dataListSize);
                        ol.add(new TableData("firstName" + counter,
                                "lastName" + counter,
                                "phone" + counter,
                                "email" + counter++));
                        dataListSize = ol.size();
                        System.out.println("Table size = " + dataListSize);
                        table.getColumns().get(0).setVisible(false);
                        table.getColumns().get(0).setVisible(true);
                    }
                });
            }
        });

        table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
        table.setItems(ol);
        borderPane.setCenter(table);
        BorderPane.setAlignment(table, Pos.CENTER);
        BorderPane.setMargin(table, new Insets(25));

        // Add columns
        TableColumn<TableData, String> c1 = new TableColumn<TableData, String>("FirstName");
        c1.setCellValueFactory(new PropertyValueFactory<TableData, String>("firstName"));
        c1.setCellFactory(new TextFieldCellFactory());

        TableColumn<TableData, String> c2 = new TableColumn<TableData, String>("LastName");
        c2.setCellValueFactory(new PropertyValueFactory<TableData, String>("lastName"));
        c2.setCellFactory(new TextFieldCellFactory());

        TableColumn<TableData, String> c3 = new TableColumn<TableData, String>("Phone");
        c3.setCellValueFactory(new PropertyValueFactory<TableData, String>("phone"));
        c3.setCellFactory(new TextFieldCellFactory());

        TableColumn<TableData, String> c4 = new TableColumn<TableData, String>("Email");
        c4.setCellValueFactory(new PropertyValueFactory<TableData, String>("email"));
        c4.setCellFactory(new TextFieldCellFactory());

        table.getColumns().addAll(c1, c2, c3, c4);

        scene.getStylesheets().add(TableViewEditingWithBinding.class.getResource("styles.css").toExternalForm());
        Stage.setScene(scene);
        Stage.show();
    }
}

我尝试添加以下代码

table.getColumns().get(0).setVisible(false);
table.getColumns().get(0).setVisible(true);
在添加行后的 addRow 按钮的处理程序中,但这没有任何帮助。

我还尝试清除支持可观察列表并将值重置为列表+添加每一行后的新行;那也没有解决问题。
非常感激任何的帮助。

4

1 回答 1

0

我有同样的问题。删除一行后,其他一些行将被空白。如果我滚动空白行更改。

我的解决方案是移动线路

setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

到单元格的构造函数并删除该行

setContentDisplay(ContentDisplay.TEXT_ONLY);

完全地。

Cell.updateItem状态的 API 文档

empty- 此单元格是否代表列表中的数据。如果它是空的,那么它不代表任何域数据,而是一个用于呈现“空”行的单元格。

对我来说,这看起来像是 JavaFX 中的一个错误,因为我的所有行都以域数据为后盾,但仍然有一些行(滚动时发生变化)接收updateItem()with empty=true

于 2013-06-02T08:00:12.023 回答