Требование:
для каждой строки. Мне нужна область, которая всегда видна сверху обычных ячеек и «зафиксирована» на внутренней стороне (либо слева, либо справа, пример ниже для слева) содержащей таблицы.Исправлено - значит придерживаться стороны, независимо от состояния горизонтальной прокрутки.
Идея:
- естественным соавтором является 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 за ссылку!