JavaFX TreeView с переключателями RadioButton, отображающими неверный статус при прокрутке - PullRequest
0 голосов
/ 17 декабря 2018

Мне нужен TreeView, в котором некоторые элементы имеют RadioButton с.Итак, я осмотрелся, использовал некоторый код из здесь и поместил туда несколько своих собственных идей.

Таким образом, в результате получается класс (RadioTreeView), который расширяет TreeViewс пользовательскими TreeItem с и TreeCell с.

Все отлично работает, кроме одной, важной вещи:

При прокрутке TreeView с одним RadioButtonвыбран, другие кнопки будут отображаться для выбора. Этот GIF демонстрирует проблему. (Я нигде не нажимаю во время прокрутки)

Short Video

Через некоторую отладку я нашелполучается, что selectedItem -свойство ToggleGroup (которое совместно используется всеми RadioButton в TreeView), фактически никогда не изменяется.И когда я добавил слушателя к каждому из RadioButton s ' selected -Property, они тоже никогда не запускались.

Так что я был озадачен и удивился, если это ошибка JavaFX илиесли я упускаю что-то очень очевидное.

Вот мой код:

import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.util.Callback;

public class RadioTreeView<T> extends TreeView<T> {
    private ToggleGroup toggleGroup; // shared by all radioButtons

    public RadioTreeView() {
        super();
        toggleGroup = new ToggleGroup();

        setCellFactory(new Callback<TreeView<T>, TreeCell<T>>() {
            @Override
            public TreeCell<T> call(TreeView<T> param) {
                return new RadioTreeCell<T>(toggleGroup);
            }
        });

    }

    public static class RadioTreeItem<T> extends TreeItem<T> {
        private final boolean hasRadio; // defines whether a RadioButton should be shown

        public RadioTreeItem(boolean hasRadio, T item) {
            super(item);
            this.hasRadio = hasRadio;
        }

        public boolean getHasRadio() {
            return hasRadio;
        }
    }

    public static class RadioTreeCell<T> extends TreeCell<T> {
        private final RadioTreeButton<T> radio = new RadioTreeButton<T>();
        private final ToggleGroup toggleGroup;

        public RadioTreeCell(ToggleGroup toggleGroup) {
            super();
            this.toggleGroup = toggleGroup;
        }

        {
            setContentDisplay(ContentDisplay.LEFT);
        }

        @Override
        public void updateItem(T item, boolean empty) {
            super.updateItem(item, empty);

            if (!empty && item != null) {
                setText(item.toString());
                if (((RadioTreeItem<T>) getTreeItem()).getHasRadio()) { // display radioButton as graphic
                    setGraphic(radio);
                    radio.setToggleGroup(toggleGroup);
                } else {
                    setGraphic(null);
                }
            } else {
                setGraphic(null);
                setText(null);
            }
        }
    }

    private static class RadioTreeButton<T> extends RadioButton {
        // non-important methods removed

        public RadioTreeButton() {
        }
    }

Любая помощь очень ценится.

PSЯ знаю, что мой код не идеален

1 Ответ

0 голосов
/ 17 декабря 2018

TreeCell с используются повторно.Это означает, что один RadioButton может использоваться в ячейке, содержащей разные TreeItem s.Вы никогда не обновляете состояние выбора RadioButton в ячейке на основе элемента.В результате тот же RadioButton остается выбранным.При прокрутке разные TreeItem помещаются в разные ячейки.Текст TableCell s обновляется, но состояние выбора их RadioButton отсутствует.

Вам необходимо каким-то образом сохранять выбранные TreeItem, например, используя свойство userData ToggleGroup:

public RadioTreeCell(ToggleGroup toggleGroup) {
    this.toggleGroup = toggleGroup;
    setContentDisplay(ContentDisplay.LEFT); // initializer code moved to constructor

    // update userData for toggleGroup, when new radio becomes selected
    radio.selectedProperty().addListener((o, oldValue, newValue) -> {
        if (newValue) {
            toggleGroup.setUserData(getTreeItem());
        }
    });
}

@Override
public void updateItem(T item, boolean empty) {
    super.updateItem(item, empty);

    if (!empty && item != null) {
        setText(item.toString());
        RadioTreeItem<T> treeItem = (RadioTreeItem<T>) getTreeItem();
        if (treeItem.getHasRadio()) { // display radioButton as graphic
            setGraphic(radio);
            radio.setToggleGroup(toggleGroup);

            // update selection based on toggleGroup userData
            radio.setSelected(toggleGroup.getUserData() == treeItem);
        } else {
            setGraphic(null);
        }
    } else {
        setGraphic(null);
        setText(null);
    }
}
...