我正在尝试创建自定义窗格,将其内容缩放到窗格的可用空间。
我创建了一个演示应用程序,它将Stage与SplitPane分开。每个拆分包含一个AutoScalePane(请参阅FMXL)。我希望AutoScalePane根据可用空间缩小/增加其内容(请使用分割栏进行播放)
当AutoScalePane边界发生变化时,AutoScalePane的内容将分组到一个组中,该组应进行缩放。
即使我收到正确的边界并且可以计算正确的缩放比例(检查调试日志),圆形节点也不会缩放..
我认为我在layoutChildren()方法中犯了一个错误,但我看不出明显的问题。
如果有更多JavaFX经验的人可以帮助我,那将会很棒。)
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("AutoScalePane Test");
primaryStage.setScene(new Scene(root, 700, 200));
primaryStage.show();
}
}
查看控制器:
public class Controller {
@FXML
public AutoScalePane scalePaneLeft;
@FXML
public AutoScalePane scalePaneRight;
@FXML
public void initialize() {
fillLeftContent();
fillRightContent();
}
private void fillLeftContent() {
Circle circle1 = new Circle(100, 300, 10);
Circle circle2 = new Circle(150, 300, 10);
Circle circle3 = new Circle(200, 300, 10);
Circle circle4 = new Circle(250, 300, 10);
scalePaneLeft.addChildren(new Node[] {circle1, circle2, circle3,
circle4});
}
private void fillRightContent() {
Circle circle1 = new Circle(100, 200, 20);
Circle circle2 = new Circle(150, 200, 20);
Circle circle3 = new Circle(200, 200, 20);
Circle circle4 = new Circle(250, 200, 20);
scalePaneRight.addChildren(new Node[] {circle1, circle2, circle3,
circle4});
}
}
FXML查看:
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import sample.AutoScalePane?>
<AnchorPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml">
<SplitPane dividerPositions="0.3" orientation="HORIZONTAL" AnchorPane.topAnchor="0" AnchorPane.bottomAnchor="0"
AnchorPane.leftAnchor="0" AnchorPane.rightAnchor="0" style="-fx-background-color: #2c5069;">
<AutoScalePane fx:id="scalePaneLeft"
style="-fx-background-color: #943736;"/>
<AutoScalePane fx:id="scalePaneRight"
style="-fx-background-color: #d27452;"/>
</SplitPane>
</AnchorPane>
自动缩放窗格:
/**
* Auto-scales its content according to the available space of the Pane.
* The content is always centered
*
*/
public class AutoScalePane extends Pane {
private Group content = new Group();
private Scale zoom = new Scale(1, 1);
public AutoScalePane() {
layoutBoundsProperty().addListener((o) -> {
autoScale();
});
content.scaleXProperty().bind(zoom.xProperty());
content.scaleYProperty().bind(zoom.yProperty());
getChildren().add(content);
}
/**
* Adds nodes to the AutoScalePane
*
* @param children nodes
*/
public void addChildren(Node... children) {
content.getChildren().addAll(children);
requestLayout();
}
private void autoScale() {
if (getHeight() > 0
&& getWidth() > 0
&& content.getBoundsInParent().getWidth() > 0
&& content.getBoundsInParent().getHeight() > 0) {
// scale
double scaleX = getWidth() / content.getBoundsInParent().getWidth();
double scaleY = getHeight() / content.getBoundsInParent()
.getHeight();
System.out.println("*************** DEBUG ****************");
System.out.println("Pane Width: " + getWidth());
System.out.println("Content Bounds Width: " + content
.getBoundsInParent()
.getWidth());
System.out.println("Pane Height: " + getHeight());
System.out.println("Content Bounds Height: " + content
.getBoundsInParent()
.getHeight());
System.out.println("ScaleX: " + scaleX);
System.out.println("ScaleY: " + scaleY);
double zoomFactor = Math.min(scaleX, scaleY);
zoom.setX(zoomFactor);
zoom.setY(zoomFactor);
requestLayout();
}
}
@Override
protected void layoutChildren() {
final double paneWidth = getWidth();
final double paneHeight = getHeight();
final double insetTop = getInsets().getTop();
final double insetRight = getInsets().getRight();
final double insetLeft = getInsets().getLeft();
final double insertBottom = getInsets().getBottom();
final double contentWidth = (paneWidth - insetLeft - insetRight) *
zoom.getX();
final double contentHeight = (paneHeight - insetTop - insertBottom) *
zoom.getY();
layoutInArea(content, 0, 0, contentWidth, contentHeight,
getBaselineOffset(), HPos.CENTER, VPos.CENTER);
}
}
答案 0 :(得分:1)
layoutChildren
。如果从layoutChildren
方法调整比例,则无需注册监听器。
至于缩放:你永远不会真正修改scale
属性。您不会在此代码段中的任何位置更新Scale
:
double zoomFactor = Math.min(zoom.getX(), zoom.getY());
zoom.setX(zoomFactor);
zoom.setY(zoomFactor);
所以zoom.getX()
和zoom.getY()
始终返回1
,它等于初始比例因子。
请注意,您可以直接将Scale
矩阵应用于内容节点的transforms
,但这不会将中心用作缩放的轴心点。
BTW:通过扩展Region
代替Pane
,您可以将对children
列表的访问权限限制为protected
,从而阻止用户修改它。
public class AutoScalePane extends Region {
private final Group content = new Group();
public AutoScalePane() {
content.setManaged(false); // avoid constraining the size by content
getChildren().add(content);
}
/**
* Adds nodes to the AutoScalePane
*
* @param children nodes
*/
public void addChildren(Node... children) {
content.getChildren().addAll(children);
requestLayout();
}
@Override
protected void layoutChildren() {
final Bounds groupBounds = content.getBoundsInLocal();
final double paneWidth = getWidth();
final double paneHeight = getHeight();
final double insetTop = getInsets().getTop();
final double insetRight = getInsets().getRight();
final double insetLeft = getInsets().getLeft();
final double insertBottom = getInsets().getBottom();
final double contentWidth = (paneWidth - insetLeft - insetRight);
final double contentHeight = (paneHeight - insetTop - insertBottom);
// zoom
double factorX = contentWidth / groupBounds.getWidth();
double factorY = contentHeight / groupBounds.getHeight();
double factor = Math.min(factorX, factorY);
content.setScaleX(factor);
content.setScaleY(factor);
layoutInArea(content, insetLeft, insetTop, contentWidth, contentHeight,
getBaselineOffset(), HPos.CENTER, VPos.CENTER);
}
}