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 ボタンのハンドラーで、しかしそれは何の助けにもなりませんでした。また、バッキングオブザーバブルリストをクリアし、値をリスト+各行が追加された後の新しい行にリセットしようとしました。それも問題を解決しませんでした。
どんな助けでも大歓迎です。