JavaFX: подгоните вертикальную полосу прокрутки к родительскому узлу в TableView

#java #css #javafx

#java #css #javafx

Вопрос:

У меня есть TableView, который прокручивается вертикально, и я хотел бы, чтобы полоса прокрутки простиралась до верха родительской панели привязки и находилась поверх заполняющего квадрата в правом верхнем углу. Смотрите ниже, на что это похоже по умолчанию. Обратите внимание, что мой узел-заполнитель белый, это не столбец таблицы в правом верхнем углу.

введите описание изображения здесь

и под этой строкой то, что я хочу, правильно реализовано другой программой.

введите описание изображения здесь

Я смог добиться этого, выполнив

 Platform.runLater(() ->
{
    ScrollBar someScrollBar = (ScrollBar) someTable.lookup(".scroll-bar:vertical");
    someScrollBar.setTranslateY(-12);
    someScrollBar.setScaleY(1.2);
}
);
  

где someTable — это TableView, созданный в FXML и упоминаемый в функции инициализации контроллера.

введите описание изображения здесь

Это выглядит нормально, но масштабируется неправильно. Если содержащая анкерная панель изменяется по вертикали, это выглядит ужасно.

Кто-нибудь может предложить лучший способ сделать это?

Большое вам спасибо за ваше время.

Комментарии:

1. вам нужен пользовательский TableViewSkin с пользовательским TableHeaderRow: последний отвечает за управление .. ну, TableHeader 🙂

Ответ №1:

Пользовательское расположение полос прокрутки не поддерживается. И мой первоначальный комментарий вам нужен пользовательский TableViewSkin с пользовательским TableHeaderRow: последний отвечает за управление .. ну, TableHeader, к сожалению, это только часть истории.

  • TableHeaderRow действительно отвечает за размещение заголовка таблицы
  • но: TableHeaderRow ничего не может сделать для разметки вертикальной полосы прокрутки — когда он пытается сделать это в переопределенном layoutChildren() , он немедленно сбрасывается
  • сброс происходит в VirtualFlow (который является родительским для полосы прокрутки)

Итак, в конце дня нам нужно

  • пользовательский TableHeaderRow, который сигнализирует о необходимости увеличения и перемещения полосы прокрутки: приведенный ниже пример устанавливает маркер, если полоса прокрутки видна (tbd: проверьте, видна ли кнопка меню), с требуемой дополнительной высотой на карте свойств полосы прокрутки
  • пользовательский VirtualFlow, который может обрабатывать маркер и фактически выполняет разметку по мере необходимости: приведенный ниже пример проверяет наличие маркера и при необходимости изменяет размер / перемещение полосы прокрутки.
  • пользовательский TableViewSkin для внедрения обоих (с помощью переопределенных заводских методов)

Пример написан для fx11, должен работать для fx10, но не для fx9, потому что последний не позволяет предоставлять пользовательский VirtualFlow:

 public class TableWithoutCorner extends Application {

    /**
     * Custom TableHeaderRow that requests a larger vbar height
     * if needed.
     */
    private static class MyTableHeader extends TableHeaderRow {

        private Region cornerAlias;
        private ScrollBar vBar;
        private TableViewSkinBase skin;

        public MyTableHeader(TableViewSkinBase skin) {
            super(skin);
            this.skin = skin;
        }

        @Override
        protected void layoutChildren() {
            super.layoutChildren();
            adjustCornerLayout();
        }

        private void adjustCornerLayout() {
            checkAlias();
            // tbd: check also if corner is visible
            if (!vBar.isVisible()) {
                vBar.getProperties().remove("DELTA");
            } else { 
                vBar.getProperties().put("DELTA", getHeight());
            }
        }

        private void checkAlias() {
            if (cornerAlias == null) {
                cornerAlias = (Region) lookup(".show-hide-columns-button");
            }
            if (vBar == null) {
                vBar = (ScrollBar) skin.getSkinnable().lookup(".scroll-bar:vertical");
            }
        }

    }

    /**
     * Custom VirtualFlow that respects additinal height for its 
     * vertical ScrollBar.
     */
    private static class MyFlow extends VirtualFlow {

        private ScrollBar vBar;
        private Region clip;

        public MyFlow() {
            // the scrollbar to adjust
            vBar = (ScrollBar) lookup(".scroll-bar:vertical");
            // the clipped container to use for accessing viewport dimensions
            clip = (Region) lookup(".clipped-container");

        }

        /**
         * Overridden to adjust vertical scrollbar's height and y-location
         * after calling super.
         */
        @Override
        protected void layoutChildren() {
            super.layoutChildren();
            adjustVBar();
        }

        /**
         * Adjusts vBar height and y-location by the height as
         * requested by the table header.
         */
        protected void adjustVBar() {
            if (vBar.getProperties().get("DELTA") == null) return;
            double delta = (double) vBar.getProperties().get("DELTA");
            vBar.relocate(clip.getWidth(), - delta);
            vBar.resize(vBar.getWidth(), clip.getHeight()   delta);
        }

    }

    /**
     * Boilerplate: need custom TableViewSkin to inject a custom TableHeaderRow and
     * custom VirtualFlow.
     */
    private static class MyTableViewSkin<T> extends TableViewSkin<T> {

        public MyTableViewSkin(TableView<T> control) {
            super(control);
        }

        @Override
        protected TableHeaderRow createTableHeaderRow() {
            return new MyTableHeader(this);
        }

        @Override
        protected VirtualFlow<TableRow<T>> createVirtualFlow() {
            return new MyFlow();
        }

    }

    private Parent createContent() {
        TableView<Locale> table = new TableView<>(FXCollections.observableArrayList(Locale.getAvailableLocales())) {

            @Override
            protected Skin<?> createDefaultSkin() {
                return new MyTableViewSkin(this);
            }

        }; 
        TableColumn<Locale, String> col = new TableColumn<>("Name");
        col.setCellValueFactory(new PropertyValueFactory<>("displayName"));
        table.getColumns().addAll(col);
        return table;
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        //stage.setTitle(FXUtils.version());
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(TableWithoutCorner.class.getName());

}