JavaFX 8自定义ListView单元是邪恶的

Dmi*_*pov 8 listview javafx javafx-8

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

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

例如,我设置类别字段的Lis​​tView项数组,如果字段类型为布尔我创建CheckBox,如果字符串我创建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();
            }
        });

    }

}
Run Code Online (Sandbox Code Playgroud)

这里是程序JDK 7的截图

在此输入图像描述

你可以看到我可以聚焦和编辑TextField.

但是,如果我用JDK 8运行这个例子,我得到了

在此输入图像描述

在这里你可以看到TextField无法集中和编辑.

jew*_*sea 10

看起来你想要一个ControlsFX PropertySheet:

的PropertySheet


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

tableproperty


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

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

基于ListView的属性编辑器的完整解决方案是一个相当复杂的事情,超出了StackOverflow答案中可合理提供的范围.

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

我认为ListView本身对于您的特定情况有一些实现问题,并且调用更新次数太多了.无论如何,您应该编写代码,以便在单个单元格上适当地处理多次调用的更新.如果你这样做,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);
    }
}
Run Code Online (Sandbox Code Playgroud)