有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JavaSwing:如何使一个组件与另一个组件保持左对齐,并相对于父组件居中

假设在JPanel中有两个组件A和B。我希望组件A保持左对齐,而组件B则最好保持在面板的中间。我模拟了以下演示(对于质量很抱歉,我是用油漆制作的): Component movement demo

我现在做的是在JPanel上使用GridBagLayout,保持a左对齐,同时保持B居中,但B在第二列中保持居中,因此它在放置a后剩余的空间中居中,而不是相对于整个面板居中

我不能为此使用任何第三方库。有没有一种方法可以使用纯Swing来实现这一点

编辑:

萤火虫的答案是正确的(如标记所示),但我创建了一个SSCCE,显示了我的原始问题(第一行)、装满鳗鱼尝试解决方案的气垫船(第二行)和萤火虫的正确解决方案(第三行)。我想贴出来也没什么害处:

package stackoverflow;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.LayoutManager2;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class StackOverflowTest extends JFrame
{

   public StackOverflowTest()
   {
      super("Stack Overflow Test");
      this.setDefaultCloseOperation(EXIT_ON_CLOSE);

      JPanel testPanel = new JPanel(new GridLayout(3, 1));

      // set up grid bag layout example
      JPanel gridBagPanel = new JPanel(new GridBagLayout());
      GridBagConstraints gridBagConstraints = new GridBagConstraints();
      gridBagConstraints.gridx = 0;
      gridBagConstraints.gridy = 0;
      gridBagConstraints.anchor = GridBagConstraints.LINE_START;
      gridBagPanel.add(getA(), gridBagConstraints);
      gridBagConstraints = new GridBagConstraints();
      gridBagConstraints.gridx = 1;
      gridBagConstraints.gridy = 0;
      gridBagConstraints.anchor = GridBagConstraints.CENTER;
      gridBagConstraints.weightx = 1.0;
      gridBagPanel.add(getB(), gridBagConstraints);
      testPanel.add(gridBagPanel);

      // set up border layout panel 
      JPanel borderPanel = new JPanel(new BorderLayout());
      borderPanel.add(getA(), BorderLayout.LINE_START);
      JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
      borderPanel.add(flowPanel, BorderLayout.CENTER);
      flowPanel.add(getB());
      testPanel.add(borderPanel);

      // set up sly493 layout panel
      JPanel sly493LayoutPanel = new JPanel(new Sly493LayoutManager());
      sly493LayoutPanel.add(getA(), Sly493LayoutManager.LEFT);
      sly493LayoutPanel.add(getB(), Sly493LayoutManager.CENTERED);
      testPanel.add(sly493LayoutPanel);

      // set up panel to act as the midpoint marker
      JPanel midpointMarkerPanel = new JPanel()
      {
         @Override
         public void paintComponent(Graphics g)
         {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;

            g2.setColor(Color.BLACK);

            int w = getWidth();
            int h = getHeight();

            int x = w / 2;
            g2.drawLine(x, 0, x, h);
            g2.drawLine(x, 0, x - (h / 5), (h / 5));
            g2.drawLine(x, 0, x + (h / 5), (h / 5));
         }
      };
      midpointMarkerPanel.setPreferredSize(new Dimension(1, 50));

      // setup up content pane
      JPanel contentPane = new JPanel(new BorderLayout());
      contentPane.add(testPanel, BorderLayout.NORTH);
      contentPane.add(midpointMarkerPanel, BorderLayout.CENTER);
      this.setContentPane(contentPane);

      pack();
   }

   private JPanel getA()
   {
      JPanel aPanel = new JPanel(new BorderLayout());
      JLabel aLabel = new JLabel("A", JLabel.CENTER);
      aLabel.setFont(aLabel.getFont().deriveFont(Font.BOLD, 36));
      aPanel.add(aLabel, BorderLayout.CENTER);
      aPanel.setBackground(Color.RED);
      aPanel.setPreferredSize(new Dimension(50, 50));
      return aPanel;
   }

   private JPanel getB()
   {
      JPanel bPanel = new JPanel();
      JLabel bLabel = new JLabel("B", JLabel.CENTER);
      bLabel.setForeground(Color.WHITE);
      bLabel.setFont(bLabel.getFont().deriveFont(Font.BOLD, 36));
      bPanel.add(bLabel, BorderLayout.CENTER);
      bPanel.setBackground(Color.BLUE);
      bPanel.setPreferredSize(new Dimension(50, 50));
      return bPanel;
   }

   public static void main(String[] args)
   {
      SwingUtilities.invokeLater(new Runnable()
      {

         @Override
         public void run()
         {
            new StackOverflowTest().setVisible(true);
         }
      });
   }

   private static class Sly493LayoutManager implements LayoutManager2
   {

      public static final Integer LEFT = 0;

      public static final Integer CENTERED = 1;

      private Component leftComponent;

      private Component centeredComponent;

      @Override
      public void addLayoutComponent(String name, Component comp)
      {
      }

      @Override
      public void removeLayoutComponent(Component comp)
      {
         if (leftComponent == comp)
         {
            leftComponent = null;
         }
         else if (centeredComponent == comp)
         {
            centeredComponent = null;
         }
      }

      @Override
      public Dimension preferredLayoutSize(Container parent)
      {
         Dimension d = new Dimension();
         for (Component c : parent.getComponents())
         {
            //wide enough to stack the left and center components horizontally without overlap
            d.width += c.getPreferredSize().width;
            //tall enough to fit the tallest component
            d.height = Math.max(d.height, c.getPreferredSize().height);
         }
         return d;
      }

      @Override
      public Dimension minimumLayoutSize(Container parent)
      {
         return preferredLayoutSize(parent);
      }

      @Override
      public void layoutContainer(Container parent)
      {
         //in this method we will:
         //1) position the left component on the left edge of the parent and center it vertically
         //2) position the center component in the center of the parent (as long as it would not overlap
         //the left component) and center it vertically

         int leftComponentWidth = leftComponent.getPreferredSize().width;
         int leftComponentHeight = leftComponent.getPreferredSize().height;
         int centeredComponentWidth = centeredComponent.getPreferredSize().width;
         int centeredComponentHeight = centeredComponent.getPreferredSize().height;

         leftComponent.setBounds(0, (parent.getHeight() - leftComponentHeight) / 2, leftComponentWidth, leftComponentHeight);
         int leftComponentRightEdge = leftComponent.getX() + leftComponent.getWidth();
         int centerComponentLeftEdge = (parent.getWidth() - centeredComponentWidth) / 2;
         int centerComponentTopEdge = (parent.getHeight() - centeredComponentHeight) / 2;

         if (leftComponentRightEdge >= centerComponentLeftEdge)
         {
            //Center component will "do its best" to remain in the center
            //but it will not do so if it would cause it to overlap the left component
            centerComponentLeftEdge = leftComponentRightEdge;
         }

         centeredComponent.setBounds(centerComponentLeftEdge,
                                     centerComponentTopEdge,
                                     centeredComponentWidth,
                                     centeredComponentHeight);
      }

      @Override
      public void addLayoutComponent(Component comp, Object constraints)
      {
         if (LEFT.equals(constraints))
         {
            if (leftComponent != null)
            {
               throw new IllegalStateException("A left component has already been assigned to this layout.");
            }
            leftComponent = comp;
         }
         else if (CENTERED.equals(constraints))
         {
            if (centeredComponent != null)
            {
               throw new IllegalStateException("A centered component has already been assigned to this layout.");
            }
            centeredComponent = comp;
         }
         else
         {
            throw new IllegalStateException("Unexpected constraints '" + constraints + "'.");
         }
      }

      @Override
      public Dimension maximumLayoutSize(Container target)
      {
         return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
      }

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

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

      @Override
      public void invalidateLayout(Container target)
      {

      }
   }
}

共 (4) 个答案

  1. # 1 楼答案

    出于实际目的,我不需要精确的对中。我需要正确的定位。我需要两排。右边的文字比中间的要短得多。因此,诀窍在于:

    1. 将两行都放在一个单独的JPanel中
    2. 使用GridBagLayout
    3. 居中项目实际上是左侧项目,但它是右对齐的

    来自真实应用程序的一些代码(过去{}块在不同的函数中,但现在这并不重要):

        JPanel myPanel = new JPanel(new GridBagLayout());
        JLabel centerFirst = new JLabel("first line");
        JLabel rightFirst = new JLabel("...");
        {
            GridBagConstraints gridBagConstraints = new GridBagConstraints();
            gridBagConstraints.gridx = 1;
            gridBagConstraints.gridy = 0;
            gridBagConstraints.anchor = GridBagConstraints.EAST;
            gridBagConstraints.weightx = 0.5;
            myPanel.add(centerFirst, gridBagConstraints);
        }
        {
            GridBagConstraints gridBagConstraints = new GridBagConstraints();
            gridBagConstraints.gridx = 3;
            gridBagConstraints.gridy = 0;
            gridBagConstraints.anchor = GridBagConstraints.EAST;
            gridBagConstraints.weightx = 0.5; // you don't really need 0.5 and 0.5, it may be 0.1 and 0.1, two equal values
            myPanel.add(rightFirst, gridBagConstraints);
        }
        JLabel centerSecond = new JLabel("second line");
        JLabel rightSecond = new JLabel("...");
        {
            GridBagConstraints gridBagConstraints = new GridBagConstraints();
            gridBagConstraints.gridx = 1;
            gridBagConstraints.gridy = 1;
            gridBagConstraints.anchor = GridBagConstraints.EAST;
            gridBagConstraints.weightx = 0.5;
            myPanel.add(centerSecond, gridBagConstraints);
        }
        {
            GridBagConstraints gridBagConstraints = new GridBagConstraints();
            gridBagConstraints.gridx = 3;
            gridBagConstraints.gridy = 1;
            gridBagConstraints.anchor = GridBagConstraints.EAST;
            gridBagConstraints.weightx = 0.5;
            myPanel.add(rightSecond, gridBagConstraints);
        }
    
        //...
    
        // the main frame uses GridBagLayout too
        JFrame frame = new JFrame();
        {
            GridBagLayout gbl = new GridBagLayout();
            //...
            frame.setLayout(gbl);
        }
    
        // myPanel: full width of the enclosing GridBagLayout
        {
            GridBagLayout gbl = (GridBagLayout) frame.getContentPane().getLayout();
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;       // it will occupy columns 0 and 1 of the GridBagLayout
            gbc.gridy = 4;       // and there will be stuff above it
            gbc.gridheight = 1;  // full width, therefore height=1 is enough
            gbc.gridwidth = 2;   // the number of columns in this GridBagLayout
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbl.setConstraints(myPanel, gbc);
            frame.add(myPanel);
        }
    
  2. # 2 楼答案

    • 让整个容器使用BorderLayout
    • 向其边框布局添加边框。行_开始位置
    • 添加另一个FlowLayout JPanel BorderLayout。居中这个小组将举行B
    • 在上面的JPanel中添加B。因为FlowLayout默认为FlowLayout。中心,B应该在这个JPanel中居中
  3. # 3 楼答案

    如果我正确理解了您的需求,那么您希望B作为一个整体相对于父对象居中,而不是在a定位后剩余的空间居中。这使得这个问题很有趣,在测试了其他建议的答案后,我认为它们不能满足这个要求

    我很难想出一种方法来组合内置的布局管理器,以实现这一点。因此,我已经破解了LayoutManager2的自定义实现

    以下可执行示例可能满足您的需要。该实现既快速又脏,绝不是一个好的通用布局管理器的示例,但它似乎满足了您的要求,并且表现得像您的图形,这让我认为它应该这样。我解释了您的要求,“B最好留在面板的中间”,这意味着B应该试图保持相对于整个面板的中心,但不以重叠的A/P>为代价。

    package com.example;
    
    import java.awt.Color;
    import java.awt.Component;
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.LayoutManager2;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Example  {
    
        public Example() {
    
            JPanel a = new JPanel();
            a.setBackground(Color.RED);
            a.setPreferredSize(new Dimension(128, 128));
    
            JPanel b = new JPanel();
            b.setBackground(Color.BLUE);
            b.setPreferredSize(new Dimension(128, 128));
    
            JPanel panel = new JPanel(new Sly493LayoutManager());
            panel.add(a, Sly493LayoutManager.LEFT);
            panel.add(b, Sly493LayoutManager.CENTERED);
    
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(panel);
            frame.pack();
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            new Example();
        }
    
        private static class Sly493LayoutManager implements LayoutManager2 {
    
            public static final Integer LEFT = 0;
    
            public static final Integer CENTERED = 1;
    
            private Component leftComponent;
    
            private Component centeredComponent;
    
            @Override
            public void addLayoutComponent(String name, Component comp) { }
    
            @Override
            public void removeLayoutComponent(Component comp) { 
                if (leftComponent == comp) {
                    leftComponent = null;
                } else if (centeredComponent == comp) {
                    centeredComponent = null;
                }           
            }
    
            @Override
            public Dimension preferredLayoutSize(Container parent) {
                Dimension d = new Dimension();
                for (Component c : parent.getComponents()) {
                    //wide enough to stack the left and center components horizontally without overlap
                    d.width += c.getPreferredSize().width;
                    //tall enough to fit the tallest component
                    d.height = Math.max(d.height, c.getPreferredSize().height);
                }
                return d;
            }
    
            @Override
            public Dimension minimumLayoutSize(Container parent) {
                return preferredLayoutSize(parent);
            }
    
            @Override
            public void layoutContainer(Container parent) {     
                //in this method we will:
                //1) position the left component on the left edge of the parent and center it vertically
                //2) position the center component in the center of the parent (as long as it would not overlap
                //the left component) and center it vertically
    
                int leftComponentWidth = leftComponent.getPreferredSize().width;
                int leftComponentHeight = leftComponent.getPreferredSize().height;
                int centeredComponentWidth = centeredComponent.getPreferredSize().width;
                int centeredComponentHeight = centeredComponent.getPreferredSize().height;
    
                leftComponent.setBounds(0, (parent.getHeight() - leftComponentHeight) / 2, leftComponentWidth, leftComponentHeight);
                int leftComponentRightEdge = leftComponent.getX() + leftComponent.getWidth();
                int centerComponentLeftEdge = (parent.getWidth() - centeredComponentWidth) / 2;
                int centerComponentTopEdge = (parent.getHeight() - centeredComponentHeight) / 2;        
    
                if (leftComponentRightEdge >= centerComponentLeftEdge) {
                    //Center component will "do its best" to remain in the center
                    //but it will not do so if it would cause it to overlap the left component
                    centerComponentLeftEdge = leftComponentRightEdge;
                }
    
                centeredComponent.setBounds(centerComponentLeftEdge, 
                        centerComponentTopEdge, 
                        centeredComponentWidth, 
                        centeredComponentHeight);
            }
    
            @Override
            public void addLayoutComponent(Component comp, Object constraints) {
                if (LEFT.equals(constraints)) {
                    if (leftComponent != null) {
                        throw new IllegalStateException("A left component has already been assigned to this layout.");
                    }
                    leftComponent = comp;
                } else if (CENTERED.equals(constraints)) {
                    if (centeredComponent != null) {
                        throw new IllegalStateException("A centered component has already been assigned to this layout.");
                    }
                    centeredComponent = comp;
                } else {
                    throw new IllegalStateException("Unexpected constraints '" + constraints + "'.");
                }
            }
    
            @Override
            public Dimension maximumLayoutSize(Container target) {
                return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
            }
    
            @Override
            public float getLayoutAlignmentX(Container target) {
                return 0;
            }
    
            @Override
            public float getLayoutAlignmentY(Container target) {
                return 0;
            }
    
            @Override
            public void invalidateLayout(Container target) {
    
            }       
        }
    }
    
  4. # 4 楼答案

    这里有一个非常快速且肮脏的方法,类似于Firefly的答案-只需创建一个布局为空的JPanel,并将两个子面板放置在其paintComponent方法中:

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class Example extends JPanel {
    
       protected JPanel a;
       protected JPanel b;
       int              size = 200;
    
       public Example() {
          setLayout( null );
    
          a = new JPanel();
          a.setBackground( Color.red );
          a.setPreferredSize( new Dimension( size, size ) );
    
          b = new JPanel();
          b.setBackground( Color.blue );
          b.setPreferredSize( new Dimension( size, size ) );
    
          add( a );
          add( b );
    
          setPreferredSize( new Dimension( 4 * size, size ) );
       }
    
       @Override
       public void paintComponent( final Graphics g ) {
          super.paintComponent( g );
          a.setBounds( 0, 0, size, size );
    
          int w = getWidth();
    
          int x = (w - size) / 2;
          if ( x < size ) {
             x = size;
          }
          b.setBounds( x, 0, size, size );
    
       }
    
       public static void main( String[] args ) {
          SwingUtilities.invokeLater( new Runnable() {
             @Override
             public void run() {
                // Create and set up the window.
                JFrame jf = new JFrame();
                jf.setName( "Example" );
                Example item = new Example();
                jf.add( item );
                // Display the window.
                jf.pack();
                jf.setVisible( true );
                jf.addWindowListener( new WindowAdapter() {
                   @Override
                   public void windowClosing( WindowEvent arg0 ) {
                      System.exit( 0 );
                   }
                } );
             }
          } );
       }
    }