哪种布局可以做到这一点?

Posted

技术标签:

【中文标题】哪种布局可以做到这一点?【英文标题】:Which layout can do this? 【发布时间】:2012-05-01 21:02:46 【问题描述】:

我正在尝试在我的应用程序中布局一些 JLabel,如下例所示:

我总是将这个 JLabel 放在中间,而其他 JLabel 的数量是可变的,可以从 1 到 30。我尝试了网格布局,方法是选择大量的列/行并将一些空的 JLabel 设置为白色空间,但我无法得到一个好的结果,也找不到如何使用MigLayout,有没有人有一个好的布局解决方案或任何其他解决方案。

PS:我不想显示圆圈,这只是为了表明 JLabel 排列在一个圆圈中。

【问题讨论】:

好问题,并且非常具有描述性(并且以字节为单位小)图像。 +1 谢谢,我想在这里连续30天后,我开始有点适应了:) 【参考方案1】:

您不需要专门支持此功能的布局管理器。您可以使用一些相当简单的三角函数自己计算 x、y 位置,然后使用常规布局,例如 SpringLayout

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SpringLayout;

public class CircleLayout 

  /**
   * Calculate x,y positions of n labels positioned in
   * a circle around a central point. Assumes AWT coordinate
   * system where origin (0,0) is top left.
   * @param args
   */
  public static void main(String[] args) 
    int n = 6;  //Number of labels
    int radius = 100;
    Point centre = new Point(200,200);

    double angle = Math.toRadians(360/n);
    List<Point> points = new ArrayList<Point>();
    points.add(centre);

    //Add points
    for (int i=0; i<n; i++) 
      double theta = i*angle;
      int dx = (int)(radius * Math.sin(theta));
      int dy = (int)(-radius * Math.cos(theta));
      Point p = new Point(centre.x + dx, centre.y + dy);
      points.add(p);
    

    draw(points);
  

  private static void draw(List<Point> points) 
    JFrame frame = new JFrame("Labels in a circle");
    frame.setSize(500, 500);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JPanel panel = new JPanel();;
    SpringLayout layout = new SpringLayout();

    int count = 0;
    for (Point point : points) 
      JLabel label = new JLabel("Point " + count);
      panel.add(label);
      count++;
      layout.putConstraint(SpringLayout.WEST, label, point.x, SpringLayout.WEST, panel);
      layout.putConstraint(SpringLayout.NORTH, label, point.y, SpringLayout.NORTH, panel);
    

    panel.setLayout(layout);

    frame.add(panel);
    frame.setVisible(true);

  

【讨论】:

【参考方案2】:

我怀疑您的要求太专业了,以至于没有 LayoutManager 可以满足您的要求。 Try creating your own!

【讨论】:

我想你已经成功了。 +1 在开始创建自定义布局管理器之前,请确保没有现有的布局管理器满足您的要求。 -> 因此,通过结合您的答案和 @Peter 的答案,我可以明白了,+1 创建自己的布局管理器看似具有挑战性,但实际上相当简单!【参考方案3】:

JH 实验室有一个ClockLayout:

这是一个为特殊目的而创建的非常愚蠢的布局。它只是将其组件从顶部顺时针排列成一个圆圈。

【讨论】:

很好的布局,但是这里没有中心元素,所以我必须实现我的自定义时钟布局。 +1【参考方案4】:

我喜欢@Baqueta 和@sacha 的想法:

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

public class CircleLayoutTest 
  public JComponent makeUI() 
    JPanel panel = new JPanel() 
      @Override protected void paintComponent(Graphics g) 
        super.paintComponent(g);
        Insets i = getInsets();
        g.translate(i.left, i.top);
        g.setColor(Color.RED);
        int w = getWidth() - i.left - i.right;
        int h = getHeight() - i.top - i.bottom;
        g.drawOval(0, 0, w, h);
        g.translate(-i.left, -i.top);
      
    ;
    panel.setLayout(new FlowLayout() 
      @Override public void layoutContainer(Container target) 
        synchronized(target.getTreeLock()) 
          int nmembers  = target.getComponentCount();
          if(nmembers<=0) return;
          Insets i = target.getInsets();
          double cx = .5 * target.getWidth();
          double cy = .5 * target.getHeight();
          Component m = target.getComponent(0);
          Dimension d = m.getPreferredSize();
          m.setSize(d.width, d.height);
          m.setLocation((int)(cx+.5-.5*d.width),(int)(cy+.5-.5*d.height));
          if(nmembers-1<=0) return;
          double rw = .5 * (target.getWidth() - i.left - i.right);
          double rh = .5 * (target.getHeight() - i.top - i.bottom);
          double x = 0, y = 0, r = 0;
          double radian = 2.0 * Math.PI / (nmembers-1);
          for(int j=1; j<nmembers; j++) 
            m = target.getComponent(j);
            if(m.isVisible()) 
              d = m.getPreferredSize();
              m.setSize(d.width, d.height);
              x = cx + rw * Math.cos(r) - .5 * d.width;
              y = cy + rh * Math.sin(r) - .5 * d.height;
              m.setLocation((int)(x+.5), (int)(y+.5));
              r += radian;
            
          
        
      
    );
    JPanel p = new JPanel(new BorderLayout());
    p.add(initPanel(panel));
    return p;
  
  private static JComponent initPanel(JComponent p) 
    p.setBorder(BorderFactory.createEmptyBorder(50,50,50,50));
    for(int i=0; i<6; i++) 
      p.add(new JLabel("No."+i));
    
    return p;
  
  public static void main(String[] args) 
    EventQueue.invokeLater(new Runnable() 
      @Override public void run() 
        createAndShowGUI();
      
    );
  
  public static void createAndShowGUI() 
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new CircleLayoutTest().makeUI());
    f.setSize(320 ,320);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  

【讨论】:

我喜欢你的大部分代码 +1,只有你的代码可以在各个方向调整大小,非常感谢 我喜欢它平滑调整大小的事实。 +1【参考方案5】:

我使用的是 Windows 窗体,因为我没有安装 Java 工具,但想法是一样的,你只需要想象你正在添加 JLabel 而不是按钮,这是一个 JFrame 或 JWindow而不是 .NET 表单。

如果我们假设一个 800 x 800 像素的区域来布局东西,代码应该是这样的

public partial class Form1 : Form

    public Form1()
    
        InitializeComponent();
        this.Load += new EventHandler(Form1_Load);
    

    void Form1_Load(object sender, EventArgs e)
    
        int numberItems = 18;
        int centreX = 400;
        int centreY = 400;


        double offset = Math.PI;
        double step = Math.PI * 2 / numberItems;


        Button b = null;
        for (int i = 0; i < numberItems; i++)
        
            b = new Button();
            b.Width = 30;
            b.Height = 30;
            SetPosition(b, 370, offset, i, step);
            this.Controls.Add(b);
        

        b = new Button();
        b.Width = 30;
        b.Height = 30;
        b.Location = new Point(centreX, centreY);
        this.Controls.Add(b);
    


    private void SetPosition(Button button, int legLength, double offset, double posOffSet, double step)
    

        int x = (int)(legLength + Math.Sin(offset + posOffSet * step) * legLength);
        int y = (int)(legLength + Math.Cos(offset + posOffSet * step) * legLength);

        button.Location = new Point(x, y);
    

【讨论】:

谢谢,但我想要一个带有布局的解决方案,因为不建议使用绝对定位。 哦,好吧,那么您不能创建自己的布局管理器来根据面板的实际宽度计算点数吗?只是一个因素类型的东西【参考方案6】:

    MigLayout 可以使用“pos x y [x2] [y2]”作为组件约束进行绝对定位。 MigLayout 确实是管理它们的布局管理器。查看他们首页上的 webstart 演示,它很好地展示了绝对定位。您仍然需要像自定义布局管理器的想法一样计算组件的位置。

    你也可以turn off the layout。

    如果您想真正发挥创意,可以查看JHotDraw。

【讨论】:

以上是关于哪种布局可以做到这一点?的主要内容,如果未能解决你的问题,请参考以下文章

移动端布局使用哪种单位比较好?

在 C# 中播放声音 - 使用哪种方式?

选择哪种分类算法?

UILabel 自动布局适合

UIViews 到垂直 3D 堆栈

iOS自动布局:给顶部空间等于superview高度的1/5