在 JavaFX 2 和 8 之间选择 TableView 期间焦点行为发生变化
Change in focus behavior during TableView selection between JavaFX 2 and 8
我有一个管理文档列表的应用程序。在 TableView 中维护的文档的一个视图显示作者、标题等。其他视图具有出版商、页数、注释、摘要等内容,具体取决于文档的类型 selected。用户通过单击 TableView 中的新行select创建活动文档。
当用户在各种视图中编辑文档信息时,随着各个控件失去焦点,更改将提交到数据库。这工作正常,直到尝试从 Java 7/JavaFX 2 切换到 8.
这里有一个SSCCE来说明问题
package focusproblem;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import static javafx.collections.FXCollections.observableArrayList;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FocusProblem extends Application {
private TextArea notesArea;
private TableView docTable;
private ObservableList<Doc> initDocs() {
ObservableList<Doc> docList = observableArrayList();
docList.add(new Doc("Harper Lee", "To Kill a Mockbird",
"Some notes on mockingbirds"));
docList.add(new Doc("John Steinbeck", "Of Mice and Men",
"Some notes about mice"));
docList.add(new Doc("Lewis Carroll", "Jabberwock",
"Some notes about jabberwocks"));
return docList;
}
private Parent initGui(ObservableList<Doc> d) {
notesArea = new TextArea();
notesArea.setId("notesArea");
notesArea.setPromptText("Add notes here");
notesArea.focusedProperty().addListener(new FocusPropertyChangeListener());
TableColumn<Doc, String> authorCol = new TableColumn<>("Author");
authorCol.setCellValueFactory(new PropertyValueFactory<Doc, String>("author"));
authorCol.setMinWidth(100.0d);
TableColumn<Doc, String> titleCol = new TableColumn<>("Title");
titleCol.setCellValueFactory(new PropertyValueFactory<Doc, String>("title"));
titleCol.setMinWidth(250.0d);
docTable = new TableView<>(d);
docTable.setPrefHeight(200.0d);
docTable.getColumns().addAll(authorCol, titleCol);
docTable.getSelectionModel().selectedItemProperty().addListener(new SelectionChangeListener());
VBox vb = new VBox();
vb.getChildren().addAll(docTable, notesArea);
return vb;
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Focus Problem");
primaryStage.setScene(new Scene(initGui(initDocs())));
primaryStage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public class SelectionChangeListener implements ChangeListener<Doc> {
@Override
public void changed(ObservableValue<? extends Doc> observable, Doc oldDoc, Doc newDoc) {
System.out.println("Changing selected row");
if (oldDoc != null) {
notesArea.textProperty().unbindBidirectional(oldDoc.notesProperty());
}
if (newDoc != null) {
notesArea.setText(newDoc.getNotes());
newDoc.notesProperty().bindBidirectional(notesArea.textProperty());
}
}
}
public class FocusPropertyChangeListener implements ChangeListener<Boolean> {
@Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean oldb, Boolean newb) {
if (ov instanceof ReadOnlyBooleanProperty) {
Object obj = ((ReadOnlyBooleanProperty) ov).getBean();
if (obj instanceof TextArea) {
TextArea ta = (TextArea) obj;
if (ta.isVisible() && !ta.isDisabled()) {
boolean b = (newb != null && newb == true);
if (b) {
System.out.println(ta.getId() + " gained focus");
} else {
System.out.println(ta.getId() + " lost focus");
Doc d = (Doc) docTable.getSelectionModel().getSelectedItem();
if (d != null) {
System.out.println(" Need to update db entry for '"
+ d.getTitle() + "' with '" + ta.getText() + "'");
}
}
}
}
}
}
}
public class Doc {
private final SimpleStringProperty author;
private final SimpleStringProperty title;
private final SimpleStringProperty notes;
public Doc(String author, String title, String notes) {
this.author = new SimpleStringProperty(this, "author", author);
this.title = new SimpleStringProperty(this, "title", title);
this.notes = new SimpleStringProperty(this, "notes", notes);
}
public void setAuthor(String value) {
author.set(value);
}
public String getAuthor() {
System.out.println("Trying to get author");
return author.get();
}
public StringProperty authorProperty() {
return author;
}
public void setTitle(String value) {
title.set(value);
}
public String getTitle() {
return title.get();
}
public StringProperty titleProperty() {
return title;
}
public void setNotes(String value) {
notes.set(value);
}
public String getNotes() {
return notes.get();
}
public StringProperty notesProperty() {
return notes;
}
}
}
当 运行 使用任一版本的 Java 编译后,程序看起来像这样(在 Windows 上):
要演示该问题,请执行以下步骤:
- Select "Of Mice and Men"
- Select笔记的TextArea(TableView下方的区域)
- 将注释文本更改为“一些 冗长 注释
关于老鼠
- 在 TableView 中,select "Jabberwock"
的行
程序用Java7编译时,控制台输出为:
Changing selected row
notesArea gained focus
notesArea lost focus
Need to update db entry for 'Of Mice and Men' with 'Some lengthy notes about mice'
Changing selected row
符合预期。数据库已更新为正确的信息。
程序用Java8编译时,控制台输出为:
Changing selected row
notesArea gained focus
Changing selected row
notesArea lost focus
Need to update db entry for 'Jabberwock' with 'Some notes about jabberwocks'
在此输出中,更新了错误的文档,并且未将真正的更改写入数据库。看起来 TableView 在 TextArea 失去焦点之前获得了焦点。
这是 Java 8 的错误或预期行为吗?任何已知的解决方法?还是我只是个笨蛋才这样做?
是的,我可以重现此行为,但我既不会将其称为错误,也不会尝试 fiddle 使用焦点系统。相反,你为什么不重写你的 SelectionChangeListener
- 没有必要关注 TextArea
:
public class SelectionChangeListener implements ChangeListener<Doc> {
@Override
public void changed(ObservableValue<? extends Doc> observable, Doc oldDoc, Doc newDoc) {
System.out.println("Changing selected row");
if (oldDoc != null) {
System.out.println(" Need to update db entry for '"
+ oldDoc.getTitle() + "' with '" + oldDoc.getNotes() + "'");
notesArea.textProperty().unbindBidirectional(oldDoc.notesProperty());
}
if (newDoc != null) {
notesArea.setText(newDoc.getNotes());
newDoc.notesProperty().bindBidirectional(notesArea.textProperty());
}
}
}
这会为您的测试用例生成以下输出:
Changing selected row
notesArea gained focus
Changing selected row
Need to update db entry for 'Of Mice and Men' with 'Some lengthy notes about mice'
notesArea lost focus
我有一个管理文档列表的应用程序。在 TableView 中维护的文档的一个视图显示作者、标题等。其他视图具有出版商、页数、注释、摘要等内容,具体取决于文档的类型 selected。用户通过单击 TableView 中的新行select创建活动文档。
当用户在各种视图中编辑文档信息时,随着各个控件失去焦点,更改将提交到数据库。这工作正常,直到尝试从 Java 7/JavaFX 2 切换到 8.
这里有一个SSCCE来说明问题
package focusproblem;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import static javafx.collections.FXCollections.observableArrayList;
import javafx.collections.ObservableList;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FocusProblem extends Application {
private TextArea notesArea;
private TableView docTable;
private ObservableList<Doc> initDocs() {
ObservableList<Doc> docList = observableArrayList();
docList.add(new Doc("Harper Lee", "To Kill a Mockbird",
"Some notes on mockingbirds"));
docList.add(new Doc("John Steinbeck", "Of Mice and Men",
"Some notes about mice"));
docList.add(new Doc("Lewis Carroll", "Jabberwock",
"Some notes about jabberwocks"));
return docList;
}
private Parent initGui(ObservableList<Doc> d) {
notesArea = new TextArea();
notesArea.setId("notesArea");
notesArea.setPromptText("Add notes here");
notesArea.focusedProperty().addListener(new FocusPropertyChangeListener());
TableColumn<Doc, String> authorCol = new TableColumn<>("Author");
authorCol.setCellValueFactory(new PropertyValueFactory<Doc, String>("author"));
authorCol.setMinWidth(100.0d);
TableColumn<Doc, String> titleCol = new TableColumn<>("Title");
titleCol.setCellValueFactory(new PropertyValueFactory<Doc, String>("title"));
titleCol.setMinWidth(250.0d);
docTable = new TableView<>(d);
docTable.setPrefHeight(200.0d);
docTable.getColumns().addAll(authorCol, titleCol);
docTable.getSelectionModel().selectedItemProperty().addListener(new SelectionChangeListener());
VBox vb = new VBox();
vb.getChildren().addAll(docTable, notesArea);
return vb;
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Focus Problem");
primaryStage.setScene(new Scene(initGui(initDocs())));
primaryStage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public class SelectionChangeListener implements ChangeListener<Doc> {
@Override
public void changed(ObservableValue<? extends Doc> observable, Doc oldDoc, Doc newDoc) {
System.out.println("Changing selected row");
if (oldDoc != null) {
notesArea.textProperty().unbindBidirectional(oldDoc.notesProperty());
}
if (newDoc != null) {
notesArea.setText(newDoc.getNotes());
newDoc.notesProperty().bindBidirectional(notesArea.textProperty());
}
}
}
public class FocusPropertyChangeListener implements ChangeListener<Boolean> {
@Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean oldb, Boolean newb) {
if (ov instanceof ReadOnlyBooleanProperty) {
Object obj = ((ReadOnlyBooleanProperty) ov).getBean();
if (obj instanceof TextArea) {
TextArea ta = (TextArea) obj;
if (ta.isVisible() && !ta.isDisabled()) {
boolean b = (newb != null && newb == true);
if (b) {
System.out.println(ta.getId() + " gained focus");
} else {
System.out.println(ta.getId() + " lost focus");
Doc d = (Doc) docTable.getSelectionModel().getSelectedItem();
if (d != null) {
System.out.println(" Need to update db entry for '"
+ d.getTitle() + "' with '" + ta.getText() + "'");
}
}
}
}
}
}
}
public class Doc {
private final SimpleStringProperty author;
private final SimpleStringProperty title;
private final SimpleStringProperty notes;
public Doc(String author, String title, String notes) {
this.author = new SimpleStringProperty(this, "author", author);
this.title = new SimpleStringProperty(this, "title", title);
this.notes = new SimpleStringProperty(this, "notes", notes);
}
public void setAuthor(String value) {
author.set(value);
}
public String getAuthor() {
System.out.println("Trying to get author");
return author.get();
}
public StringProperty authorProperty() {
return author;
}
public void setTitle(String value) {
title.set(value);
}
public String getTitle() {
return title.get();
}
public StringProperty titleProperty() {
return title;
}
public void setNotes(String value) {
notes.set(value);
}
public String getNotes() {
return notes.get();
}
public StringProperty notesProperty() {
return notes;
}
}
}
当 运行 使用任一版本的 Java 编译后,程序看起来像这样(在 Windows 上):
要演示该问题,请执行以下步骤:
- Select "Of Mice and Men"
- Select笔记的TextArea(TableView下方的区域)
- 将注释文本更改为“一些 冗长 注释 关于老鼠
- 在 TableView 中,select "Jabberwock" 的行
程序用Java7编译时,控制台输出为:
Changing selected row
notesArea gained focus
notesArea lost focus
Need to update db entry for 'Of Mice and Men' with 'Some lengthy notes about mice'
Changing selected row
符合预期。数据库已更新为正确的信息。
程序用Java8编译时,控制台输出为:
Changing selected row
notesArea gained focus
Changing selected row
notesArea lost focus
Need to update db entry for 'Jabberwock' with 'Some notes about jabberwocks'
在此输出中,更新了错误的文档,并且未将真正的更改写入数据库。看起来 TableView 在 TextArea 失去焦点之前获得了焦点。
这是 Java 8 的错误或预期行为吗?任何已知的解决方法?还是我只是个笨蛋才这样做?
是的,我可以重现此行为,但我既不会将其称为错误,也不会尝试 fiddle 使用焦点系统。相反,你为什么不重写你的 SelectionChangeListener
- 没有必要关注 TextArea
:
public class SelectionChangeListener implements ChangeListener<Doc> {
@Override
public void changed(ObservableValue<? extends Doc> observable, Doc oldDoc, Doc newDoc) {
System.out.println("Changing selected row");
if (oldDoc != null) {
System.out.println(" Need to update db entry for '"
+ oldDoc.getTitle() + "' with '" + oldDoc.getNotes() + "'");
notesArea.textProperty().unbindBidirectional(oldDoc.notesProperty());
}
if (newDoc != null) {
notesArea.setText(newDoc.getNotes());
newDoc.notesProperty().bindBidirectional(notesArea.textProperty());
}
}
}
这会为您的测试用例生成以下输出:
Changing selected row
notesArea gained focus
Changing selected row
Need to update db entry for 'Of Mice and Men' with 'Some lengthy notes about mice'
notesArea lost focus