最大化窗口和布局过程



因此,这里的目标是为ScrollPane使用自定义ScrollBar,而在最大化/最小化窗口时不会出现布局问题。

考虑示例程序:

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);
}
}

当内容(2个节点p1p2)超出界限时,它将生成左侧带有自定义ScrollBarScrollPaneGUI。为了便于测试,当用鼠标拖动p1p2时,它们都可以调整大小(试试看)。虽然ScrollBar出现并按预期工作,但如果我们开始最大化和最小化窗口,则布局中存在缺陷。

例如:

  1. 启动程序
  2. 调整内容大小以使ScrollBar出现,但不要太大很多(这样当你最大化它时,它就会在边界内)
  3. 最大化窗口-您会注意到栏可能已经消失,但"空白"出现
  4. 现在,若您以某种方式进行刷新(例如,通过再次调整内容大小),栏将由于布局过程更新而消失

其他错误:

  1. 启动程序并最大化
  2. 调整内容大小,使其仍然包含在最大化的窗口中,但足够大,当您最小化时,它将超出界限
  3. 最小化
  4. 请注意ScrollBar是如何错位的

如果你想尝试其他东西,几乎没有其他错误,但所有错误都源于这样一个事实,即当你最大化/最小化这种情况时(使用第一个例子):

  1. ScrollBar是管理的且可见的(考虑到内容超过)
  2. 最大化
  3. 计算窗口大小和布局(使用旧值-managed=true&&visible=true)
  4. 布局发生,一切就绪,所有属性都收到更新,包括sb.visibleAmountProperty()使ScrollBar集为托管集,并对false可见(由于它们已绑定,请参阅代码)
  5. ScrollBar变为不可见且不受管理,但布局已发生,不会重新运行

如何使用窗口最大化?否则,我如何绑定ScrollBar,使其在最大化时不会中断?请注意,我们谈论的是最大化,而不是调整大小(这很有效)。

我找到了一个解决方法,但这似乎不是绝对最好的方法,如果有合适的方法,请分享。

sb.managedProperty().addListener(e -> Platform.runLater(() -> content.requestLayout()));

由于这将在"稍后"运行(而且也在第一个应该完成上一个布局的主线程上),这将导致重新发布。

为什么它不是最好的?首先,当不真正需要(而不是最大化)时,它也会被调用,导致双重布局(我们不能用一些if语句来解决这个问题)。

最新更新