TableRow: как расположить «фиксированную» область на левой стороне? - PullRequest
0 голосов
/ 13 февраля 2019

Требование:

для каждой строки. Мне нужна область, которая всегда видна сверху обычных ячеек и «зафиксирована» на внутренней стороне (либо слева, либо справа, пример ниже для слева) содержащей таблицы.Исправлено - значит придерживаться стороны, независимо от состояния горизонтальной прокрутки.

Идея:

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

Проблема:

реализация идеи оставляет один пиксель (или около того) слева, который не покрыт фиксированной областью.Чтобы воспроизвести, запустите пример, убедитесь, что полоса прокрутки видна и достаточно места для прокрутки, чтобы прокрутить видимый текст ячеек под левой частью таблицы - этот текст отображается в небольшом промежутке слева от фиксированной области.Контекст на fx11 на win10 с hdpi ... на всякий случай, он не появляется повсюду.

Вопросы:

  • Что не так, мой макет, некоторая ошибка ядра отсутствует(или слишком много) округление пикселей?
  • Как исправить?

Пример:

public class PlainTableViewDebugRowLayoutWithOverlaySO extends Application {

    public static class MyTableRowSkin<T> extends TableRowSkin<T> {

        ScrollBar hbar;
        StackPane overlay;
        public MyTableRowSkin(TableRow<T> control) {
            super(control);
            overlay = new StackPane();
            overlay.getStyleClass().setAll("overlay");
            getChildren().add(overlay);
        }

        @Override
        protected void layoutChildren(double x, double y, double w, double h) {
            super.layoutChildren(x, y, w, h);
            layoutOverlay(x, y, w, h);
        }

        private void layoutOverlay(double x, double y, double w, double h) {
            if (getVisibleLeafColumns().isEmpty()) return;
            ScrollBar scrollBar = getHorizontalScrollBar();
            if (scrollBar == null)  return;

            double hbarValue = scrollBar.getValue();
            TableColumnBase<T, ?> firstColumn = getVisibleLeafColumns().get(0);
            overlay.resize(firstColumn.getWidth(), h);
            overlay.relocate(hbarValue, 0);
            if (!getChildren().contains(overlay)) {
                // children are cleared on change notification from columns, re-add
                getChildren().add(overlay);
            }
            overlay.toFront();
        }

        protected ScrollBar getHorizontalScrollBar() {
            if (hbar == null) {
                VirtualFlow flow = getVirtualFlow();
                if (flow != null) {
                    hbar = (ScrollBar) invokeGetMethodValue(VirtualFlow.class, flow, "getHbar");
                    registerChangeListener(hbar.valueProperty(), e -> getSkinnable().requestLayout());
                }
            }
            return hbar;
        }

        // copied from TableRowSkinBase
        protected VirtualFlow getVirtualFlow() {
            Parent p = getSkinnable();
            while (p != null) {
                if (p instanceof VirtualFlow) {
                    return (VirtualFlow) p;
                }
                p = p.getParent();
            }
            return null;
        }

    }

    private Parent createContent() {
        TableView<Locale> table = createPlainTable();
        table.setRowFactory(c -> {
            return new TableRow<>() {

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

            };
        });
        BorderPane content = new BorderPane(table);
        return content;
    }

    private TableView<Locale> createPlainTable() {
        List<Locale> locales = Arrays.stream(Locale.getAvailableLocales())
                // want to have visible content
                .filter(l -> l.getDisplayCountry() != null && !l.getDisplayCountry().trim().isEmpty())
                // just a few
                .limit(10)
                .collect(Collectors.toList());
        TableView<Locale> table =  new TableView<>(FXCollections.observableList(locales));
        table.getColumns().addAll(
                createColumn("country"),
                createColumn("displayLanguage"), 
                createColumn("displayLanguage"), 
                createColumn("language"), 
                createColumn("displayCountry")
                ); 
        return table;
    }

    private TableColumn<Locale, String> createColumn(String property) {
        TableColumn<Locale, String> column = new TableColumn<>(property);
        column.setCellValueFactory(new PropertyValueFactory<>(property));
        return column;
    }
    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        URL uri = getClass().getResource("overlaycell.css");
        stage.getScene().getStylesheets().add(uri.toExternalForm());

        //stage.setTitle(FXUtils.version());
        stage.show();
    }

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

    public static Object invokeGetMethodValue(Class declaringClass, Object target, String name) {
        try {
            Method field = declaringClass.getDeclaredMethod(name);
            field.setAccessible(true);
            return field.invoke(target);
        } catch (SecurityException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

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

}

CSS для оверлея:

.overlay
{
    -fx-background-color: lavenderblush;
}

Обновление: вероятно такой же, как ошибка , упомянутая в «исправлении» в ControlsFx SpreadSheet , спасибо JosePereda за ссылку!

...