JavaFX 8 Custom ListView Cells it's Evil

﹥>﹥吖頭↗ 提交于 2019-12-04 03:15:48
jewelsea

Looks like you want a ControlsFX PropertySheet:


A similar implementation, is in the answer to JavaFX 2 TableView : different cell factory depending on the data inside the cell. Even though the answer to that question is based on TableView, both ListView and TableView virtualized controls, so the implementation concept is somewhat similar to using a ListView with an HBox as outlined in your question.


Update based on sample code from question.

I still think that a ControlsFX PropertySheet appears to be the most appropriate solution for what you are trying to accomplish. Using a Virtualized Control like a ListView for such a task simply makes things more complicated than they need to be.

A full solution for a ListView based property editor is a pretty complex thing and outside the scope of what can be reasonably provided in a StackOverflow answer.

The sample code you provide in your question has some issues. The ListView is a virtualized control so you should not create new graphic nodes to place inside the ListView all the time that update is being called. What happens is that a TextField gets focus, then update is called on the ListView and you create a new TextField and by default the new TextField does not have focus.

I think the ListView itself has some implementation issues for your particular case and is calling update too many times. Regardless of this, you should write your code so that you can appropriately handle update being called multiple times on a single cell and. if you do so, it won't matter if ListView calls your method more times than it needs to.

Here is some sample code which may help you make more progress. I don't believe the sample code is a complete solution to your issue, it is certainly not provided as a comprehensive property editor for Java objects, but perhaps it may give you some inspiration to help improve and implement your code (assuming you decide to continue attempting to solve this problem in this manner). If you continue using ListView, you may want to look into the editing routines for ListView (similar to what is defined for editing table cells in the Oracle JavaFX tutorials).

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);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!