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

时间:2013-11-14 12:25:48

标签: listview javafx javafx-8

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

  1. 带有名称字段的标签
  2. 必须从对象的上下文生成
  3. 例如我设置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的截图

    enter image description here

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

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

    enter image description here

    在这里你可以看到TextField无法进行聚焦和编辑。

1 个答案:

答案 0 :(得分:10)

看起来你想要一个ControlsFX PropertySheet

propertysheet


类似的实现是JavaFX 2 TableView : different cell factory depending on the data inside the cell的答案。即使这个问题的答案是基于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教程中为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);
    }
}