Но предполагается ли, что переключение значения этого свойства может повлиять на поведение основного макета? По крайней мере, для стандартных элементов управления?
Проблема:
В сценариях, когда ширина TableRow на 1 пиксель больше ширины VirtualFlow, отображается горизонтальная полоса прокрутки VirtualFlow. Это действительно так, и я с этим согласен (как показано на скриншоте ниже). На рисунке ниже ширина VirtualFlow составляет 307 пикселей, а ширина TableRow – 308 пикселей.
[img]https: //i.sstatic.net/LPH0Ukdr.png[/img]
В приведенном выше сценарии, когда я перетаскиваю ползунок полосы прокрутки, я ожидаю перемещения TableRow на один пиксель. Но этого не происходит. На самом деле, когда я перетаскиваю ползунок, ничего не происходит (ни содержимое не перемещается, ни сам ползунок).
При тщательном исследовании выяснилось, что ширина дорожки полосы прокрутки такая же, как у ширину большого пальца, что приводит к игнорированию события перетаскивания.
Ниже приведен код события перетаскивания большого пальца в классе ScrollBarSkin, где вы можете заметить условие if, позволяющее пропустить вычисления. если длина большого пальца не меньше длины дорожки.
thumb.setOnMouseDragged(me -> {
if (me.isSynthesized()) {
// touch-screen events handled by Scroll handler
me.consume();
return;
}
/*
** if max isn't greater than min then there is nothing to do here
*/
if (getSkinnable().getMax() > getSkinnable().getMin()) {
/*
** if the tracklength isn't greater then do nothing....
*/
if (trackLength > thumbLength) {
Point2D cur = thumb.localToParent(me.getX(), me.getY());
if (dragStart == null) {
// we're getting dragged without getting a mouse press
dragStart = thumb.localToParent(me.getX(), me.getY());
}
double dragPos = getSkinnable().getOrientation() == Orientation.VERTICAL ? cur.getY() - dragStart.getY(): cur.getX() - dragStart.getX();
behavior.thumbDragged(preDragThumbPos + dragPos / (trackLength - thumbLength));
}
me.consume();
}
});
Теперь, когда я дальше исследую, почему длины дорожки и ползунка одинаковы, я заметил, что на самом деле это код вычисления длины ползунка, который фиксирует вычисленное значение.
thumbLength = snapSizeX(Utils.clamp(minThumbLength(), (trackLength * visiblePortion), trackLength));
В приведенной выше строке вычисленное значение trackLength составляет 289,0 пикселей, а вычисленное значение ThumbLength – 288,06168831168833 пикселей. Но поскольку это значение привязано, оно округляется до 289,0 пикселей, что равно trackLength.
Решение/обходное решение:
Из приведенного выше наблюдения видно, что свойство snapToPixel элемента ScrollBar влияет на вычисленные значения. Поэтому я создал собственный VirtualFlow для доступа к полосам прокрутки и отключения свойства snapToPixel.
class CustomVirtualFlow extends VirtualFlow {
public CustomVirtualFlow() {
getHbar().setSnapToPixel(false);
getVbar().setSnapToPixel(false);
}
}
Как только я включил эту настройку, полоса прокрутки стала активной, и когда я перетаскиваю большой палец, он перемещает мой контент. Вы можете заметить разницу на рисунке ниже.

Итак, мой вопрос: является ли это предполагаемым поведением, имеющим snapToPixel=true по умолчанию, что приводит к ненужному отображению полосы прокрутки, и я обязан отключить свойства snapToPixel? Другими словами, это ошибка JavaFX или нет?
Ниже приведен демонстрационный код:
import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.skin.TableViewSkin;
import javafx.scene.control.skin.VirtualFlow;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
public class HorizontalScrollBarIssueDemo extends Application {
String CSS = "data:text/css," +
"""
.label{
-fx-font-weight: bold;
}
""";
@Override
public void start(final Stage stage) throws Exception {
final VBox root = new VBox();
root.setSpacing(30);
root.setPadding(new Insets(10));
final Scene scene = new Scene(root, 500, 350);
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.setTitle("Horizontal ScrollBar Issue " + System.getProperty("javafx.runtime.version"));
stage.show();
VBox defaultBox = new VBox(10, new Label("Default TableView"), getTable(false));
VBox customBox = new VBox(10, new Label("TableView whose scrollbar's snapToPixel=false"), getTable(true));
root.getChildren().addAll(defaultBox, customBox);
}
private TableView getTable(boolean custom) {
TableView tableView;
if (custom) {
tableView = new TableView() {
@Override
protected Skin createDefaultSkin() {
return new TableViewSkin(this) {
@Override
protected VirtualFlow createVirtualFlow() {
return new CustomVirtualFlow();
}
};
}
};
} else {
tableView = new TableView();
}
tableView.setMaxHeight(98);
tableView.setMaxWidth(309);
VBox.setVgrow(tableView, Priority.ALWAYS);
int colCount = 4;
for (int i = 0; i < colCount; i++) {
final int index = i;
TableColumn column = new TableColumn("Option " + i);
column.setCellValueFactory(param -> new ReadOnlyObjectWrapper(param.getValue().get(index)));
tableView.getColumns().add(column);
}
for (int j = 0; j < 1; j++) {
List row = new ArrayList();
for (int i = 0; i < colCount; i++) {
row.add("Row" + j + "-Opt" + i);
}
tableView.getItems().add(row);
}
return tableView;
}
class CustomVirtualFlow extends VirtualFlow {
public CustomVirtualFlow() {
getHbar().setSnapToPixel(false);
getVbar().setSnapToPixel(false);
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/790 ... -behaviour