Java蜗牛线布局管理器

Java蜗牛线布局管理器,java,swing,layout,layout-manager,cyclic,Java,Swing,Layout,Layout Manager,Cyclic,我正在寻找一个java自定义布局管理器,它以类似于snail的后端的方式排列一些数据(例如JLabel) 到目前为止,我已经尝试过在互联网上找到的圆形布局进行定制,但没有成功。。有什么想法吗?你可以自己写布局。螺旋公式我从。它是阿基米德式的,而蜗牛更像蜗牛,您可以更改calculatePoint()方法以返回不同的螺旋 注意:这个布局效率有点低,因为它每次都重新计算所有组件的位置,而不是缓存它 import java.awt.*; import java.util.*; import javax

我正在寻找一个java自定义布局管理器,它以类似于snail的后端的方式排列一些数据(例如JLabel)


到目前为止,我已经尝试过在互联网上找到的圆形布局进行定制,但没有成功。。有什么想法吗?

你可以自己写布局。螺旋公式我从。它是阿基米德式的,而蜗牛更像蜗牛,您可以更改
calculatePoint()
方法以返回不同的螺旋

注意:这个布局效率有点低,因为它每次都重新计算所有组件的位置,而不是缓存它

import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;

public class SpiralLayout implements LayoutManager2 {

    private enum Size { MIN, MAX, PREF }

    private double radiusStep;
    private double angleStep;

    public SpiralLayout() {
        this(10, Math.toRadians(15.0));
    }

    public SpiralLayout(double radius, double stepSize) {
        this.radiusStep = radius;
        this.angleStep = stepSize;
    }

    public void setRadiusStep(double radiusStep) {
        this.radiusStep = radiusStep;
    }


    public void setAngleStep(double angleStep) {
        this.angleStep = angleStep;
    }

    @Override
    public void addLayoutComponent(String name, Component comp) {
        // calculated on the fly
    }

    @Override
    public void removeLayoutComponent(Component comp) {
        // calculated on the fly
    }

    @Override
    public Dimension preferredLayoutSize(Container parent) {
        return getSize(parent, Size.PREF);
    }

    private Dimension getSize(Container parent, Size size) {
        doLayoutContainer(parent, Short.MAX_VALUE, Short.MAX_VALUE, size);

        Point min = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
        Point max = new Point(0, 0);
        for(Component component : parent.getComponents()) {
            Dimension preferredSize = getSize(component, size);
            min.x = Math.min(min.x, component.getX());
            min.y = Math.min(min.y, component.getY());
            max.x = Math.max(max.x, component.getX() + preferredSize.width);
            max.y = Math.max(max.y, component.getY() + preferredSize.height);
        }
        int center = Short.MAX_VALUE / 2;
        return new Dimension(
                Math.max(Math.abs(center - min.x), Math.abs(center - max.x) * 2),
                Math.max(Math.abs(center - min.y), Math.abs(center - max.y) * 2));
    }

    private Dimension getSize(Component component, Size size) {
        switch(size) {
        case MAX:
            return component.getMaximumSize();
        case MIN:
            return component.getMinimumSize();
        case PREF:
            return component.getPreferredSize();
        default:
            assert false : "Unknown size: " + size;
            return new Dimension();
        }
    }

    @Override
    public Dimension minimumLayoutSize(Container parent) {
        return getSize(parent, Size.MIN);
    }

    @Override
    public void layoutContainer(Container parent) {
        doLayoutContainer(parent,
                parent.getWidth(), parent.getHeight(), Size.PREF);
    }

    private List<Rectangle> doLayoutContainer(Container parent,
            int width, int height, Size size) {

        Point offset = new Point(width / 2, height / 2);
        List<Rectangle> componentBounds = new ArrayList<Rectangle>();
        double angle = 0;
        double radius = 1;
        for(Component component : parent.getComponents()) {
            Dimension preferredSize = getSize(component, size);
            Rectangle bounds;
            do {
                bounds = new Rectangle(
                        add(calculatePoint(angle, radius), offset),
                        preferredSize);
                angle += angleStep;
                radius += radiusStep;
            }
            while(overlaps(bounds, componentBounds));

            component.setBounds(bounds);
            componentBounds.add(bounds);
        }
        return componentBounds;
    }

    private Point calculatePoint(double angle, double radius) {
        double x = radius * Math.cos(angle);
        double y = radius * Math.sin(angle);
        return new Point((int) x, (int) y);
    }

    private boolean overlaps(Rectangle bounds, List<Rectangle> componentBounds) {
        for(Rectangle other : componentBounds) {
            if(other.intersects(bounds)) {
                return true;
            }
        }
        return false;
    }

    private Point add(Point a, Point b) {
        return new Point(a.x + b.x, a.y + b.y);
    }

    @Override
    public void addLayoutComponent(Component comp, Object constraints) {
        // calculated on the fly
    }

    @Override
    public Dimension maximumLayoutSize(Container parent) {
        return getSize(parent, Size.MAX);
    }

    @Override
    public float getLayoutAlignmentX(Container target) {
        return 0.5f;
    }

    @Override
    public float getLayoutAlignmentY(Container target) {
        return 0.5f;
    }

    @Override
    public void invalidateLayout(Container target) {
        // calculated on the fly
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                final SpiralLayout layout = new SpiralLayout();
                final JPanel panel = new JPanel(layout);

                final JSpinner angleSpinner = new JSpinner(new SpinnerNumberModel(Math.toDegrees(layout.angleStep), 1.0, 360.0, 5.0));
                angleSpinner.addChangeListener(new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        double angle = (Double) angleSpinner.getValue();
                        layout.setAngleStep(Math.toRadians(angle));
                        panel.revalidate();
                    }
                });
                final JSpinner radiusSpinner = new JSpinner(new SpinnerNumberModel((int)  layout.radiusStep, 1, 1000, 1));
                radiusSpinner.addChangeListener(new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        int radius = (Integer) radiusSpinner.getValue();
                        layout.setRadiusStep(radius);
                        panel.revalidate();
                    }
                });
                JPanel buttons = new JPanel();
                buttons.add(new JLabel("Radius step:"));
                buttons.add(radiusSpinner);
                buttons.add(new JLabel("Angle step"));
                buttons.add(angleSpinner);

                for(int i = 1; i <= 25; i++) {
                    panel.add(new JLabel("Label " + i));
                }
                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.getContentPane().add(buttons, BorderLayout.PAGE_START);
                frame.getContentPane().add(panel);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}
import java.awt.*;
导入java.util.*;
导入javax.swing.*;
导入javax.swing.event.*;
公共类SpiralLayout实现LayoutManager2{
私有枚举大小{MIN,MAX,PREF}
私有双半径步骤;
私人双阶梯;
公共空间布局(){
这(10,数学托拉迪安(15.0));
}
公共螺旋布局(双半径、双步长){
this.radiustep=半径;
此.angleStep=步长;
}
公共无效setRadiusStep(双radiusStep){
this.radiustep=radiustep;
}
公共无效setAngleStep(双angleStep){
this.angleStep=angleStep;
}
@凌驾
public void addLayoutComponent(字符串名称,组件组成){
//即时计算
}
@凌驾
公共void removeLayoutComponent(组件组件组件){
//即时计算
}
@凌驾
公共维度preferredLayoutSize(容器父级){
返回getSize(父级,Size.PREF);
}
私有维度getSize(容器父级,大小){
doLayoutContainer(父级,Short.MAX_值,Short.MAX_值,大小);
最小点=新点(Integer.MAX\u值,Integer.MAX\u值);
最大点=新点(0,0);
对于(组件:parent.getComponents()){
维度preferredSize=getSize(组件,大小);
min.x=Math.min(min.x,component.getX());
min.y=Math.min(min.y,component.getY());
max.x=Math.max(max.x,component.getX()+preferredSize.width);
max.y=Math.max(max.y,component.getY()+preferredSize.height);
}
int center=Short.MAX_值/2;
返回新维度(
数学最大值(数学绝对值(中心-最小值x)、数学绝对值(中心-最大值x)*2),
max(Math.abs(center-min.y)、Math.abs(center-max.y)*2);
}
私有维度getSize(组件、大小){
开关(尺寸){
最大情况:
返回组件。getMaximumSize();
案例最小值:
返回组件。getMinimumSize();
案例序言:
返回组件。getPreferredSize();
违约:
断言false:“未知大小:”+大小;
返回新维度();
}
}
@凌驾
公共维度minimumLayoutSize(容器父级){
返回getSize(父级,Size.MIN);
}
@凌驾
公共无效布局容器(容器父级){
doLayoutContainer(母公司、,
parent.getWidth()、parent.getHeight()、Size.PREF);
}
私有列表doLayoutContainer(容器父级,
整数宽度、整数高度、大小){
点偏移=新点(宽度/2,高度/2);
List componentBounds=新的ArrayList();
双角度=0;
双半径=1;
对于(组件:parent.getComponents()){
维度preferredSize=getSize(组件,大小);
矩形边界;
做{
边界=新矩形(
添加(计算点(角度、半径)、偏移),
首选尺寸);
角度+=角度步长;
半径+=半径步长;
}
while(重叠(边界、组件边界));
组件。立根(边界);
componentBounds.add(bounds);
}
返回组件边界;
}
专用点计算点(双角度、双半径){
双x=半径*数学cos(角度);
双y=半径*数学sin(角度);
返回新点((int)x,(int)y);
}
专用布尔重叠(矩形边界、列表组件边界){
用于(矩形其他:组件边界){
如果(其他相交(边界)){
返回true;
}
}
返回false;
}
专用点添加(点a、点b){
返回新点(a.x+b.x,a.y+b.y);
}
@凌驾
public void addLayoutComponent(组件组件、对象约束){
//即时计算
}
@凌驾
公共维度maximumLayoutSize(容器父级){
返回getSize(父级,Size.MAX);
}
@凌驾
公共浮点getLayoutAlignmentX(容器目标){
返回0.5f;
}
@凌驾
公共浮点GetLayoutAlignment(容器目标){
返回0.5f;
}
@凌驾
public void invalidateLayout(容器目标){
//即时计算
}
公共静态void main(字符串[]args){
invokeLater(新的Runnable(){
@凌驾
公开募捐{
最终螺旋布局=新的螺旋布局();
最终JPanel面板=新JPanel(布局);
最终JSpinner angleSpinner=新JSpinner(新SpinnerNumberModel(数学toDegrees(layout.angleStep),1.0360.0,5.0));
angleSpinner.addChangeListener(新的ChangeListener(){
@凌驾
公共无效状态已更改(更改事件e){
双角度=(双)角度微调器.getValue();
布局。setAngleStep(数学toRadians(角度));
panel.revalidate();
}
});
最终JSpinner radiusSpinner=新J