我有自定义ListView单元,我有两个控件的HBox:
例如我设置ListView项类字段数组,如果字段类型为Boolean我创建CheckBox,如果String我创建TextField等。
问题是:我只在方法updateItem()中获取Field对象 - 只有在那里我才能创建我的控件。
在JavaFX 7中,一切正常,但在Java 8上 - 我没有看到我的控件。
存在解决我案件的正确方法吗?
更新1
这里是复制问题的完整示例:
package javafx8listviewexample;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Date;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.DatePicker;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.util.Callback;
/**
*
* @author dmitrynelepov
*/
public class FXMLDocumentController implements Initializable {
static class TestClassForListView {
public String fieldString;
public Date fieldDate;
}
static class MyListCell extends ListCell<Field> {
/**
* As in tutorial
*
* @param t
* @param bln
*/
@Override
protected void updateItem(Field t, boolean bln) {
super.updateItem(t, bln);
if (t != null) {
if (t.getType().equals(String.class)) {
System.out.println("Draw string");
setGraphic(new TextField(t.getName()));
} else if (t.getType().equals(Date.class)) {
System.out.println("Draw Date");
setGraphic(new DatePicker());
}
}
}
}
@FXML
private ListView<Field> listView;
@FXML
private void handleButtonAction(ActionEvent event) {
this.listView.getItems().clear();
this.listView.setItems(
FXCollections.observableArrayList(
TestClassForListView.class.getFields()
)
);
}
@Override
public void initialize(URL url, ResourceBundle rb) {
this.listView.setCellFactory(new Callback<ListView<Field>, ListCell<Field>>() {
@Override
public ListCell<Field> call(ListView<Field> p) {
return new MyListCell();
}
});
}
}
程序JDK 7的截图
你可以看到我可以聚焦和编辑TextField。
但是如果我用JDK 8运行这个例子我得到了
在这里你可以看到TextField无法进行聚焦和编辑。
答案 0 :(得分:10)
看起来你想要一个ControlsFX PropertySheet:
类似的实现是JavaFX 2 TableView : different cell factory depending on the data inside the cell的答案。即使这个问题的答案是基于TableView,ListView和TableView都是虚拟化控件,所以实现概念有点类似于在你的问题中使用带有HBox的ListView。
根据问题的示例代码进行更新。
我仍然认为ControlsFX PropertySheet似乎是您尝试完成的最合适的解决方案。使用像ListView这样的虚拟控件来完成这样的任务只会让事情变得更加复杂。
基于ListView的属性编辑器的完整解决方案非常复杂,超出了StackOverflow答案中可合理提供的范围。
您在问题中提供的示例代码存在一些问题。 ListView是一个虚拟化控件,因此您不应创建新的图形节点,以便在调用更新时始终放在ListView中。会发生什么是TextField获得焦点,然后在ListView上调用update并创建一个新的TextField,默认情况下新的TextField没有焦点。
我认为ListView本身对您的特定情况有一些实现问题,并且调用更新次数太多了。无论如何,您应该编写代码,以便在单个单元格上适当地处理多次调用的更新。如果你这样做,ListView调用你的方法的次数多于它需要的时间无关紧要。
以下是一些示例代码,可以帮助您取得更多进展。我不相信示例代码是您问题的完整解决方案,它肯定不是作为Java对象的综合属性编辑器提供的,但它可能会给您一些灵感来帮助改进和实现您的代码(假设您决定继续尝试以这种方式解决这个问题)。如果继续使用ListView,您可能需要查看ListView的编辑例程(类似于Oracle JavaFX教程中为editing table cells定义的内容)。
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
import java.lang.reflect.Field;
import java.time.LocalDate;
/**
* @author dmitrynelepov
* Modified by Jewelsea
*/
public class EvilHasSurvived extends Application {
static class TestClassForListView {
public String fieldString;
public LocalDate fieldDate;
@Override
public String toString() {
return "TestClassForListView{" +
"fieldString='" + fieldString + '\'' +
", fieldDate=" + fieldDate +
'}';
}
}
static class MyListCell extends ListCell<Field> {
private TextField textField;
private DatePicker datePicker;
private Object editedObject;
private ChangeListener<Boolean> editCommitHandler;
public MyListCell(Object editedObject) {
this.editedObject = editedObject;
setContentDisplay(ContentDisplay.RIGHT);
}
@Override
protected void updateItem(Field t, boolean bln) {
super.updateItem(t, bln);
if (datePicker != null) {
datePicker.focusedProperty().removeListener(editCommitHandler);
}
if (textField != null) {
textField.focusedProperty().removeListener(editCommitHandler);
}
if (t == null) {
setText(null);
setGraphic(null);
return;
}
if (t.getType().equals(String.class)) {
if (textField == null) {
textField = new TextField();
}
editCommitHandler = (observable, oldValue, newValue) -> {
try {
t.set(editedObject, textField.getText());
System.out.println(editedObject + " for " + textField + " value " + textField.getText());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
};
textField.focusedProperty().addListener(editCommitHandler);
setText(t.getName());
setGraphic(textField);
} else if (t.getType().equals(LocalDate.class)) {
if (datePicker == null) {
datePicker = new DatePicker();
}
editCommitHandler = (observable, oldValue, newValue) -> {
try {
t.set(editedObject, datePicker.getValue());
System.out.println(editedObject + " for " + datePicker + " value " + datePicker.getValue());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
};
datePicker.focusedProperty().addListener(editCommitHandler);
setText(t.getName());
setGraphic(datePicker);
}
}
}
@Override
public void start(Stage stage) throws Exception {
ListView<Field> listView = new ListView<>();
listView.setItems(
FXCollections.observableArrayList(
TestClassForListView.class.getFields()
)
);
TestClassForListView testObject = new TestClassForListView();
listView.setCellFactory(p -> new MyListCell(testObject));
stage.setScene(new Scene(listView));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}