将节点布置成圆形

Lay nodes out in circle

我正在尝试这样布置我的节点: 这是我当前的布局,名为 CircularPane:

import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.layout.Pane;

public class CircularPane extends Pane {
    @Override
    protected void layoutChildren() {
        final int radius = 50;
        final double increment = 360 / getChildren().size();
        double degreese = 0;
        for (Node node : getChildren()) {
            double x = radius * Math.cos(Math.toRadians(degreese)) + getWidth() / 2;
            double y = radius * Math.sin(Math.toRadians(degreese)) + getHeight() / 2;
            layoutInArea(node, x - node.getBoundsInLocal().getWidth() / 2, y - node.getBoundsInLocal().getHeight() / 2, getWidth(), getHeight(), 0.0, HPos.LEFT, VPos.TOP);
            degreese += increment;
        }
    }
}

这是我的主要内容 class:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        CircularPane pane = new CircularPane();
        for(int i = 0; i < 6; i++) {
            Button button = new Button("" + i);
            pane.getChildren().add(button);
        }
        stage.setScene(new Scene(pane));
        stage.show();
    }
}

这是我当前的显示:

节点不在底部接触,它们均匀分布在圆周上。我想做到这一点,让他们走到底部,但不知道该怎么做。

您将按钮布局在一个圆圈上的方法是正确的,但在这一行中您定义了它们的布局方式:

final double increment = 360 / getChildren().size();

这给出了从圆心参考的任意两个按钮之间的相同角度!这就是您获得当前显示的原因。

如果你想像你图中那样布置节点,如果我做对了,这些是条件:

  • 每个节点的圆心都在圆上
  • 节点在水平方向上等距分开:水平间距从 0 到某个值。
  • 从圆到第一个节点的初始间隙从 0 到某个值。
  • 可以调整每个节点的大小以满足前面的条件

所以让我们为这些值定义一些字段,并调整窗格的大小:

class CircularPane extends Pane {

    private final double radius;
    private final double ext_gap;
    private final double int_gap;

    public CircularPane(double radius, double ext_gap, double int_gap){
        this.radius=radius;
        this.ext_gap=ext_gap;
        this.int_gap=int_gap;

        setMinSize(2*radius, 2d*radius);
        setPrefSize(2*radius, 2d*radius);
        setMaxSize(2*radius, 2d*radius);
    }
}

现在,给定任何 n 个按钮,上述条件可以转化为一个求解节点大小的方程式。如果总可用长度 (2*radius) 减去两个外部间隙 (2*ext_gap) 与大小为 buttonSizen-1 的内部间隙 (int_size) 的 n 个按钮相同), 那么每个按钮的大小必须是:

@Override
protected void layoutChildren() {
    int n=getChildren().size();
    double buttonSize = (2*radius-2*ext_gap-(n-1)*int_gap)/n;
}

最后,现在您可以设置按钮的大小和布局每个节点,只需增加 x 坐标(按按钮的大小加上内部间隙),然后得到 y圆方程坐标:

@Override
protected void layoutChildren() {
    int n=getChildren().size();
    double buttonSize = (2*radius-2*ext_gap-(n-1)*int_gap)/n;

    double x=ext_gap+buttonSize/2d, y;            
    for (Node node : getChildren()) {
        ((Button)node).setMinSize(buttonSize, buttonSize);
        ((Button)node).setPrefSize(buttonSize, buttonSize);
        ((Button)node).setMaxSize(buttonSize, buttonSize);

        node.setStyle("-fx-font-size: "+Math.round(buttonSize/3));
        node.setManaged(false);

        y=getHeight()/2d+Math.sqrt(radius*radius-Math.pow(x-radius,2d));

        layoutInArea(node, x-buttonSize/2d, y-buttonSize/2d, getWidth(), getHeight(), 0.0, HPos.LEFT, VPos.TOP);

        x+=buttonSize+int_gap;
    }

}

请注意,您还可以更改字体的大小,以获得任何大小的按钮的可见数字。

另请注意,node.setManaged(false); 避免在您单击按钮时调用 layoutChildren()(由于在获得焦点或单击时单击按钮的大小发生变化)。

最后这将创建圆形窗格并绘制一个圆:

@Override
public void start(Stage primaryStage) {
    CircularPane pane = new CircularPane(200,20,10);
    for(int i = 0; i < 6; i++) {
        Button button = new Button("" + (i+1));
        pane.getChildren().add(button);
    }
    Circle circle = new Circle(200);
    circle.setFill(null);
    circle.setStroke(Color.BLACK);
    StackPane stack=new StackPane(circle,pane);
    Scene scene = new Scene(stack, 500, 500);

    primaryStage.setScene(scene);
    primaryStage.show();
}

结果如下: