Ячейки JavaFX 8 CustomViewView - это зло

У меня есть пользовательская ячейка ListView У меня есть HBox с двумя элементами управления:

  1. Метка с полем имени
  2. Это должно генерироваться из контекста объекта

Например, я устанавливаю массив элементов ListView полей класса, если тип поля Boolean, я создаю CheckBox, если String, я создаю TextField и т. Д.

Проблема в том, что я получаю объект Field только в методе updateItem() - и только там я могу создавать свои элементы управления.

В 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 не может быть сфокусирован и отредактирован.

1 ответ

Решение

Похоже, вы хотите ControlSFX PropertySheet:

PropertySheet


Аналогичная реализация в ответе на JavaFX 2 TableView: разные фабрики ячеек в зависимости от данных внутри ячейки. Несмотря на то, что ответ на этот вопрос основан на TableView, виртуализированных элементах управления ListView и TableView, поэтому концепция реализации отчасти похожа на использование ListView с HBox, как описано в вашем вопросе.

tableproperty


Обновление на основе примера кода из вопроса.

Я все еще думаю, что таблица свойств ControlsFX представляется наиболее подходящим решением для того, чего вы пытаетесь достичь. Использование виртуализированного элемента управления, такого как ListView, для такой задачи просто усложняет ситуацию.

Полное решение для редактора свойств на основе ListView - довольно сложная вещь, выходящая за рамки того, что может быть разумно предоставлено в ответе Stackru.

Пример кода, который вы предоставляете в своем вопросе, имеет некоторые проблемы. ListView является виртуализированным элементом управления, поэтому вам не следует создавать новые графические узлы для размещения внутри ListView все время, когда вызывается обновление. Что происходит, так это то, что TextField получает фокус, затем вызывается update для ListView, и вы создаете новый 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);
    }
}
Другие вопросы по тегам