java jpanel layout layout-manager swing

其他 - java Swing:如何使一个组件与另一个组件相对于父组件居中对齐

发布于 2021-03-28 23:34:21

假设我在JPanel中有两个组件,A和B。我希望组件A保持对齐,而组件B尽其所能保持在面板中间。我模拟了以下演示(质量不好意思,我用油漆涂了): 组件运动演示

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

我不能为此使用任何第三方库。有没有办法使用纯Swing做到这一点?

编辑:

Firefly的答案是正确的(已标记),但是我创建了一个SSCCE,显示了我的原始问题(第一行),Hovercraft Full Of Eels的尝试解决方案(第二行)和Firefly的正确解决方案(第三行)。我认为发布它不会有什么坏处:

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)
      {

      }
   }
}

查看更多

提问者
sly493
被浏览
0
Firefly 2015-06-22 23:37

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

我在想办法将内置布局管理器以一种可以实现该目标的方式组合在一起时遇到麻烦。因此,我破解了LayoutManager2的自定义实现。

以下可执行文件示例可以满足你的需求。该实现既快捷又肮脏,绝不是优秀的通用布局管理器的示例,但它似乎可以满足你的要求,并且其行为就像你的图纸让我认为应该那样。我解释了你的要求,即“ B尽最大努力留在面板中间”的意思是,B应该尝试相对于整个面板保持居中,但不要以重叠A为代价。

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) {

        }       
    }
}