8

我有自定义 ListView 单元格,我有带有两个控件的 HBox:

  1. 带有名称字段的标签
  2. 它必须从对象的上下文中生成

例如,我设置类字段的 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 不能被聚焦和编辑。

4

1 回答 1

11

看起来你想要一个 ControlsFX PropertySheet

属性表


JavaFX 2 TableView的答案中有一个类似的实现:不同的单元工厂取决于单元内的数据。即使该问题的答案基于 TableView,ListView 和 TableView 都是虚拟化控件,因此实现概念有点类似于使用带有 HBox 的 ListView,如您的问题中所述。

表属性


根据问题中的示例代码进行更新。

我仍然认为 ControlsFX PropertySheet 似乎是您想要完成的最合适的解决方案。为这样的任务使用像 ListView 这样的虚拟化控件只会使事情变得比需要的更复杂。

基于 ListView 的属性编辑器的完整解决方案是一件非常复杂的事情,并且超出了 StackOverflow 答案中可以合理提供的范围。

您在问题中提供的示例代码存在一些问题。ListView 是一个虚拟化控件,因此您不应在调用更新时始终创建新的图形节点以放置在 ListView 内。发生的情况是 TextField 获得焦点,然后在 ListView 上调用 update 并创建一个新的 TextField ,默认情况下新的 TextField 没有焦点。

我认为 ListView 本身对于您的特定情况有一些实现问题,并且调用 update 太多次。无论如何,您应该编写代码,以便您可以适当地处理在单个单元格上多次调用更新。如果您这样做,ListView 调用您的方法的次数是否超过它需要的次数并不重要。

这是一些示例代码,可以帮助您取得更大的进步。我不相信示例代码是您问题的完整解决方案,它肯定不是作为 Java 对象的综合属性编辑器提供的,但也许它可能会给您一些灵感来帮助改进和实现您的代码(假设您决定继续尝试以这种方式解决此问题)。如果您继续使用 ListView,您可能需要查看 ListView 的编辑例程(类似于Oracle JavaFX 教程中为编辑表格单元定义的内容)。

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);
    }
}
于 2013-11-14T16:55:57.697 回答