此处的目标是为ScrollBar
使用自定义ScrollPane
,而不会在最大化/最小化窗口时遇到布局问题。
考虑示例程序:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Layout extends Application
{
@Override
public void start(Stage stage)
{
BorderPane main = new BorderPane();
main.setPrefSize(800, 600);
BorderPane center = new BorderPane(); // begin center
center.setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY)));
main.setCenter(center); // end center
BorderPane left = new BorderPane(); // begin left
ScrollPane pane = new ScrollPane();
pane.setFitToWidth(true);
Pane p1 = new Pane(); // child 1
p1.setPrefSize(200, 100);
p1.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
this.makeResizable(p1);
Pane p2 = new Pane(); // child 2
p2.setPrefSize(200, 100);
p2.setBackground(new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY)));
this.makeResizable(p2);
VBox content = new VBox(10, p1, p2); // content in scroll pane
pane.setContent(content);
// replace normal bars
pane.setHbarPolicy(ScrollBarPolicy.NEVER);
pane.setVbarPolicy(ScrollBarPolicy.NEVER);
// with custom
ScrollBar sb = new ScrollBar();
sb.setOrientation(Orientation.VERTICAL);
sb.minProperty().bind(pane.vminProperty());
sb.maxProperty().bind(pane.vmaxProperty());
sb.visibleAmountProperty().bind(pane.heightProperty().divide(content.heightProperty()));
sb.managedProperty().bind(sb.visibleAmountProperty().lessThan(1.0)); // bar should be managed when it is needed (content too long)
sb.visibleProperty().bind(sb.managedProperty()); // and also visible only when managed
sb.valueProperty().bindBidirectional(pane.vvalueProperty());
left.setCenter(pane); // content
left.setRight(sb); // scroll bar
main.setLeft(left); //end left
Scene scene = new Scene(main);
stage.setScene(scene);
stage.show();
}
// Simple for testing
double prevY;
boolean dragging;
// Makes node resizable on drag.
private void makeResizable(Region region)
{
region.addEventFilter(MouseEvent.MOUSE_PRESSED, e ->
{
this.dragging = true;
region.setPrefHeight(region.getHeight());
this.prevY = e.getSceneY();
});
region.addEventFilter(MouseEvent.MOUSE_DRAGGED, e ->
{
if (!this.dragging) return;
region.setPrefHeight(region.getPrefHeight() + (e.getSceneY() - this.prevY));
this.prevY = e.getSceneY();
});
region.addEventFilter(MouseEvent.MOUSE_RELEASED, e -> this.dragging = false);
}
public static void main(String[] args)
{
launch(args);
}
}
它会在左侧生成带有ScrollPane
的GUI,并在内容(2个节点ScrollBar
和p1
)超出边界时显示自定义p2
。为了便于测试 - 使用鼠标拖动它们时,p1
和p2
都可以调整大小(试一试)。虽然ScrollBar
出现并按预期工作,但如果我们开始最大化和最小化窗口,则布局存在缺陷。
例如:
ScrollBar
,但不是
很多(当你最大化它时,它会在界限内)其他错误:
ScrollBar
是如何放错位置的。如果您尝试其他东西,还有很少的其他错误,但所有这些都源于这样一个事实:当您最大化/最小化时(使用第一个示例):
ScrollBar
受管理且可见(考虑到内容超出)。sb.visibleAmountProperty()
使ScrollBar
设置为托管且可见为false(因为它们已绑定,请参阅代码)。ScrollBar
变得不可见且不受管理,但布局已经发生,并且不会重新运行。如何使窗口最大化?我怎么能绑定ScrollBar
所以它在最大化时不会破坏?请注意,我们谈论的是最大化,而不是调整大小(有效)。
答案 0 :(得分:0)
我找到了一个解决方法,但这似乎不是绝对最好的事情,如果有更合适的 - 请分享。
sb.managedProperty().addListener(e -> Platform.runLater(() -> content.requestLayout()));
由于这将在“稍后”运行(并且因为也在第一个应完成先前布局的主线程上),这将导致重新布局发生。
为什么不是最好的?好吧,如果没有真正需要(虽然不是最大化)会导致双重布局(我们不能用if语句真正重新调整它)。