我有一个管理文档列表的应用程序。在TableView中维护的文档的一个视图显示作者,标题等。其他视图具有诸如发布者,页数,注释,摘要等的内容,具体取决于所选文档的类型。用户通过单击TableView中的新行来选择活动文档。
当用户在各种视图中编辑文档信息时,由于各个控件失去焦点,因此更改将提交到数据库。这种方法很好,直到尝试从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上):
要演示此问题,请执行以下步骤:
使用Java 7编译程序时,控制台输出为:
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
正如所料。使用正确的信息更新数据库。
使用Java 8编译程序时,控制台输出为:
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中的错误或预期行为吗?任何已知的解决方法?或者我只是这样做的傻瓜?
答案 0 :(得分:2)
是的我可以重现这种行为,但我既不会将其称为错误,也不会试图摆弄焦点系统。相反,为什么不重写你的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