隐形组件仍占用JPanel空间

时间:2011-10-11 14:33:13

标签: java swing user-interface jpanel

我在JPanel中将一系列组件放在一起作为GridLayout。 我需要暂时隐藏组件,但是setVisible(false)没有剪切它,因为组件之间仍然存在空隙。

有一种快速简便的方法吗?或者我是否必须保持JPanel的状态,删除组件,然后恢复它?

SSCCE:

[GridLayout2.java]

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Insets;

public class GridLayout2 extends GridLayout 
{
  public GridLayout2() {
    this(1, 0, 0, 0);
  }

  public GridLayout2(int rows, int cols) {
    this(rows, cols, 0, 0);
  }

  public GridLayout2(int rows, int cols, int hgap, int vgap) {
    super(rows, cols, hgap, vgap);
  }

  public Dimension preferredLayoutSize(Container parent) {
    //System.err.println("preferredLayoutSize");
    synchronized (parent.getTreeLock()) {
      Insets insets = parent.getInsets();
      int ncomponents = parent.getComponentCount();
      int nrows = getRows();
      int ncols = getColumns();
      if (nrows > 0) {
        ncols = (ncomponents + nrows - 1) / nrows;
      } 
      else {
        nrows = (ncomponents + ncols - 1) / ncols;
      }
      int[] w = new int[ncols];
      int[] h = new int[nrows];
      for (int i = 0; i < ncomponents; i ++) {
        int r = i / ncols;
        int c = i % ncols;
        Component comp = parent.getComponent(i);
        Dimension d = comp.getPreferredSize();
        if (w[c] < d.width) {
          w[c] = d.width;
        }
        if (h[r] < d.height) {
          h[r] = d.height;
        }
      }
      int nw = 0;
      for (int j = 0; j < ncols; j ++) {
        nw += w[j];
      }
      int nh = 0;
      for (int i = 0; i < nrows; i ++) {
        nh += h[i];
      }
      return new Dimension(insets.left + insets.right + nw + (ncols-1)*getHgap(), 
          insets.top + insets.bottom + nh + (nrows-1)*getVgap());
    }
  }

  public Dimension minimumLayoutSize(Container parent) {
    System.err.println("minimumLayoutSize");
    synchronized (parent.getTreeLock()) {
      Insets insets = parent.getInsets();
      int ncomponents = parent.getComponentCount();
      int nrows = getRows();
      int ncols = getColumns();
      if (nrows > 0) {
        ncols = (ncomponents + nrows - 1) / nrows;
      } 
      else {
        nrows = (ncomponents + ncols - 1) / ncols;
      }
      int[] w = new int[ncols];
      int[] h = new int[nrows];
      for (int i = 0; i < ncomponents; i ++) {
        int r = i / ncols;
        int c = i % ncols;
        Component comp = parent.getComponent(i);
        Dimension d = comp.getMinimumSize();
        if (w[c] < d.width) {
          w[c] = d.width;
        }
        if (h[r] < d.height) {
          h[r] = d.height;
        }
      }
      int nw = 0;
      for (int j = 0; j < ncols; j ++) {
        nw += w[j];
      }
      int nh = 0;
      for (int i = 0; i < nrows; i ++) {
        nh += h[i];
      }
      return new Dimension(insets.left + insets.right + nw + (ncols-1)*getHgap(), 
          insets.top + insets.bottom + nh + (nrows-1)*getVgap());
    }
  }

  public void layoutContainer(Container parent) {
    //System.err.println("layoutContainer");
    synchronized (parent.getTreeLock()) {
      Insets insets = parent.getInsets();
      int ncomponents = parent.getComponentCount();
      int nrows = getRows();
      int ncols = getColumns();
      if (ncomponents == 0) {
        return;
      }
      if (nrows > 0) {
        ncols = (ncomponents + nrows - 1) / nrows;
      } 
      else {
        nrows = (ncomponents + ncols - 1) / ncols;
      }
      int hgap = getHgap();
      int vgap = getVgap();
      // scaling factors      
      Dimension pd = preferredLayoutSize(parent);
      double sw = (1.0 * parent.getWidth()) / pd.width;
      double sh = (1.0 * parent.getHeight()) / pd.height;
      // scale
      int[] w = new int[ncols];
      int[] h = new int[nrows];
      for (int i = 0; i < ncomponents; i ++) {
        int r = i / ncols;
        int c = i % ncols;
        Component comp = parent.getComponent(i);
        Dimension d = comp.getPreferredSize();
        d.width = (int) (sw * d.width);
        d.height = (int) (sh * d.height);
        if (w[c] < d.width) {
          w[c] = d.width;
        }
        if (h[r] < d.height) {
          h[r] = d.height;
        }
      }
      for (int c = 0, x = insets.left; c < ncols; c ++) {
        for (int r = 0, y = insets.top; r < nrows; r ++) {
          int i = r * ncols + c;
          if (i < ncomponents) {
            parent.getComponent(i).setBounds(x, y, w[c], h[r]);
          }
          y += h[r] + vgap;
        }
        x += w[c] + hgap;
      }
    }
  }  
}

[SSCCE.java]

import java.awt.Color;
import javax.swing.*;
import javax.swing.border.*;

public class SSCCE extends JFrame{

    JPanel innerPane = new JPanel();
    JScrollPane scr  = new JScrollPane(innerPane);

    public static void main(String[] args) {
        new SSCCE();
    }


    public SSCCE() {

        setSize(400, 800);
        innerPane.setLayout(new GridLayout2(0, 1));

        add(scr);

        for (int i = 0; i < 30; i++)
        {
            innerPane.add(getPane());
        }

        setVisible(true);

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {}

        for (int i = 0; i < 30; i++)
        {
            if (i%2==0)
                innerPane.getComponent(i).setVisible(false);
        }

    }


    private JPanel getPane()
    {
        JPanel ret = new JPanel();
        JLabel lbl = new JLabel("This is a pane.");

        ret.add(lbl);
        ret.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
        ret.setBackground(Color.gray);

        return ret;

    }

}

screenshot

6 个答案:

答案 0 :(得分:10)

  

因为组件之间仍然存在空隙。

是的,GridLayout并不那么聪明。它只使用组件总数来确定行/列的数量。

  

有一种快速简便的方法吗?

我会创建一个自定义布局管理器。只需复制GridLayout代码并进行一些更改。基本的变化是:

  1. 覆盖ncomponents变量。您需要循环使用所有组件并计算可见组件,而不是仅仅使用面板上的组件数量。

  2. 在布局代码中,您需要添加if (visible)支票。

  3. 编辑:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.border.*;
    
    public class InvisibleGridLayout implements LayoutManager, java.io.Serializable
    {
        int hgap;
        int vgap;
        int rows;
        int cols;
    
        public InvisibleGridLayout() {
        this(1, 0, 0, 0);
        }
    
        public InvisibleGridLayout(int rows, int cols) {
        this(rows, cols, 0, 0);
        }
    
        public InvisibleGridLayout(int rows, int cols, int hgap, int vgap) {
        if ((rows == 0) && (cols == 0)) {
            throw new IllegalArgumentException("rows and cols cannot both be zero");
        }
        this.rows = rows;
        this.cols = cols;
        this.hgap = hgap;
        this.vgap = vgap;
        }
    
        public int getRows() {
        return rows;
        }
    
        public void setRows(int rows) {
        if ((rows == 0) && (this.cols == 0)) {
            throw new IllegalArgumentException("rows and cols cannot both be zero");
        }
        this.rows = rows;
        }
    
        public int getColumns() {
        return cols;
        }
    
        public void setColumns(int cols) {
        if ((cols == 0) && (this.rows == 0)) {
            throw new IllegalArgumentException("rows and cols cannot both be zero");
        }
        this.cols = cols;
        }
    
        public int getHgap() {
        return hgap;
        }
    
        public void setHgap(int hgap) {
        this.hgap = hgap;
        }
    
        public int getVgap() {
        return vgap;
        }
    
        public void setVgap(int vgap) {
        this.vgap = vgap;
        }
    
        public void addLayoutComponent(String name, Component comp) {
        }
    
        public void removeLayoutComponent(Component comp) {
        }
    
        public Dimension preferredLayoutSize(Container parent) {
          synchronized (parent.getTreeLock()) {
        Insets insets = parent.getInsets();
    //  int ncomponents = parent.getComponentCount();
        int ncomponents = getVisibleComponents(parent);
        int nrows = rows;
        int ncols = cols;
    
        if (nrows > 0) {
            ncols = (ncomponents + nrows - 1) / nrows;
        } else {
            nrows = (ncomponents + ncols - 1) / ncols;
        }
        int w = 0;
        int h = 0;
    //  for (int i = 0 ; i < ncomponents ; i++) {
        for (int i = 0 ; i < parent.getComponentCount(); i++) {
            Component comp = parent.getComponent(i);
    
            if (!comp.isVisible()) continue; // added
    
            Dimension d = comp.getPreferredSize();
            if (w < d.width) {
            w = d.width;
            }
            if (h < d.height) {
            h = d.height;
            }
        }
    
        Dimension d = new Dimension(insets.left + insets.right + ncols*w + (ncols-1)*hgap,
                     insets.top + insets.bottom + nrows*h + (nrows-1)*vgap);
    
        return d;
          }
        }
    
        public Dimension minimumLayoutSize(Container parent) {
          synchronized (parent.getTreeLock()) {
            Insets insets = parent.getInsets();
    //  int ncomponents = parent.getComponentCount();
        int ncomponents = getVisibleComponents(parent);
        int nrows = rows;
        int ncols = cols;
    
        if (nrows > 0) {
            ncols = (ncomponents + nrows - 1) / nrows;
        } else {
            nrows = (ncomponents + ncols - 1) / ncols;
        }
        int w = 0;
        int h = 0;
    //  for (int i = 0 ; i < ncomponents ; i++) {
        for (int i = 0 ; i < parent.getComponentCount(); i++) {
            Component comp = parent.getComponent(i);
    
            if (!comp.isVisible()) continue; // added
    
            Dimension d = comp.getMinimumSize();
            if (w < d.width) {
            w = d.width;
            }
            if (h < d.height) {
            h = d.height;
            }
        }
    
        Dimension d = new Dimension(insets.left + insets.right + ncols*w + (ncols-1)*hgap,
                     insets.top + insets.bottom + nrows*h + (nrows-1)*vgap);
    
        return d;
          }
        }
    
        public void layoutContainer(Container parent) {
          synchronized (parent.getTreeLock()) {
        Insets insets = parent.getInsets();
    //  int ncomponents = parent.getComponentCount();
        int ncomponents = getVisibleComponents(parent);
        int nrows = rows;
        int ncols = cols;
        boolean ltr = parent.getComponentOrientation().isLeftToRight();
    
        if (ncomponents == 0) {
            return;
        }
        if (nrows > 0) {
            ncols = (ncomponents + nrows - 1) / nrows;
        } else {
            nrows = (ncomponents + ncols - 1) / ncols;
        }
    
    //  int w = parent.width - (insets.left + insets.right);
    //  int h = parent.height - (insets.top + insets.bottom);
        int w = parent.getSize().width - (insets.left + insets.right);
        int h = parent.getSize().height - (insets.top + insets.bottom);
        w = (w - (ncols - 1) * hgap) / ncols;
        h = (h - (nrows - 1) * vgap) / nrows;
    /*
        if (ltr) {
            for (int c = 0, x = insets.left ; c < ncols ; c++, x += w + hgap) {
            for (int r = 0, y = insets.top ; r < nrows ; r++, y += h + vgap) {
                int i = r * ncols + c;
                if (i < ncomponents) {
                parent.getComponent(i).setBounds(x, y, w, h);
                }
            }
            }
        } else {
    //      for (int c = 0, x = parent.width - insets.right - w; c < ncols ; c++, x -= w + hgap) {
            for (int c = 0, x = parent.getSize().width - insets.right - w; c < ncols ; c++, x -= w + hgap) {
            for (int r = 0, y = insets.top ; r < nrows ; r++, y += h + vgap) {
                int i = r * ncols + c;
                if (i < ncomponents) {
                parent.getComponent(i).setBounds(x, y, w, h);
                }
            }
            }
        }
          }
    */
    
            int i = 0;
    
            if (ltr)
            {
                for (int r = 0, y = insets.top ; r < nrows ; r++, y += h + vgap)
                {
                    int c = 0;
                    int x = insets.left;
    
                    while (c < ncols)
                    {
                        if (i >= parent.getComponentCount()) break;
    
                        Component component = parent.getComponent(i);
    
                        if (component.isVisible())
                        {
                            parent.getComponent(i).setBounds(x, y, w, h);
                            c++;
                            x += w + hgap;
                        }
    
                        i++;
                    }
                }
            }
    
        }}
    
        private int getVisibleComponents(Container parent)
        {
            int visible = 0;
    
            for (Component c: parent.getComponents())
            {
                if (c.isVisible())
                    visible++;
            }
    
            return visible;
        }
    
        public String toString() {
        return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap +
                               ",rows=" + rows + ",cols=" + cols + "]";
        }
    
    
    
        public static void main(String[] args)
        {
            final JPanel innerPane = new JPanel();
            JScrollPane scr  = new JScrollPane(innerPane);
    
            innerPane.setLayout(new InvisibleGridLayout(0, 3));
    
    
            for (int i = 0; i < 30; i++)
            {
                JPanel ret = new JPanel();
                JLabel lbl = new JLabel("This is  pane " + i);
    
                ret.add(lbl);
                ret.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
                ret.setBackground(Color.gray);
    
                innerPane.add(ret);
            }
    
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(scr);
            frame.setBounds(400, 0, 400, 700);
            frame.setVisible(true);
    
            javax.swing.Timer timer = new javax.swing.Timer(2000, new ActionListener()
            {
                public void actionPerformed(ActionEvent e)
                {
                    for (int i = 0; i < 30; i++)
                    {
                        if (i%2==0)
                            innerPane.getComponent(i).setVisible(false);
                    }
    
                }
            });
            timer.setRepeats(false);
            timer.start();
    
        }
    }
    

答案 1 :(得分:4)

以下是我的三个方面。

Hide Components

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

class HideComponents {

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JPanel gui = new JPanel(new BorderLayout());
                JToolBar tb = new JToolBar();
                gui.add(tb, BorderLayout.NORTH);
                final JButton openTool = new JButton("Open");
                final JButton saveTool = new JButton("Save");
                tb.add( openTool );
                tb.add( saveTool );

                JPanel buttonFlow = new JPanel(new FlowLayout(3));
                gui.add(buttonFlow, BorderLayout.CENTER);
                final JButton openFlow = new JButton("Open");
                final JButton saveFlow = new JButton("Save");
                buttonFlow.add( openFlow );
                buttonFlow.add( saveFlow );

                JPanel buttonBox = new JPanel();
                gui.add(buttonBox, BorderLayout.EAST);
                BoxLayout bl = new BoxLayout(buttonBox, BoxLayout.Y_AXIS);
                buttonBox.setLayout(bl);
                final JButton openBox = new JButton("Open");
                final JButton saveBox = new JButton("Save");
                buttonBox.add( openBox );
                buttonBox.add( saveBox );

                final JCheckBox openChoice = new JCheckBox("Show open", true);
                openChoice.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ae) {
                        openTool.setVisible(openChoice.isSelected());
                        openFlow.setVisible(openChoice.isSelected());
                        openBox.setVisible(openChoice.isSelected());
                    }
                });
                gui.add(openChoice, BorderLayout.SOUTH);

                JOptionPane.showMessageDialog(null, gui);
            }
        });
    }
}

反思

请考虑交换:

button.setVisible(false);

有关:

button.setEnabled(false);

对于查看GUI的大多数用户来说,这将更加直观,并且具有相同的最终效果。

可见:

Disable Components

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

class DisableComponents {

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JPanel gui = new JPanel(new BorderLayout());
                JToolBar tb = new JToolBar();
                gui.add(tb, BorderLayout.NORTH);
                final JButton openTool = new JButton("Open");
                final JButton saveTool = new JButton("Save");
                tb.add( openTool );
                tb.add( saveTool );

                JPanel buttonFlow = new JPanel(new FlowLayout(3));
                gui.add(buttonFlow, BorderLayout.CENTER);
                final JButton openFlow = new JButton("Open");
                final JButton saveFlow = new JButton("Save");
                buttonFlow.add( openFlow );
                buttonFlow.add( saveFlow );

                JPanel buttonBox = new JPanel();
                gui.add(buttonBox, BorderLayout.EAST);
                BoxLayout bl = new BoxLayout(buttonBox, BoxLayout.Y_AXIS);
                buttonBox.setLayout(bl);
                final JButton openBox = new JButton("Open");
                final JButton saveBox = new JButton("Save");
                buttonBox.add( openBox );
                buttonBox.add( saveBox );

                final JCheckBox openChoice = new JCheckBox("Enable open", true);
                openChoice.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent ae) {
                        openTool.setEnabled(openChoice.isSelected());
                        openFlow.setEnabled(openChoice.isSelected());
                        openBox.setEnabled(openChoice.isSelected());
                    }
                });
                gui.add(openChoice, BorderLayout.SOUTH);

                JOptionPane.showMessageDialog(null, gui);
            }
        });
    }
}

答案 2 :(得分:3)

在EDT期间不要致电Thread.sleep(int);,因为阻止EDT,请使用javax.swing.Timer

例如

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.border.*;

public class SSCCE extends JFrame {

    private static final long serialVersionUID = 1L;
    private JPanel innerPane = new JPanel();
    private JScrollPane scr = new JScrollPane(innerPane);
    private Timer timer;

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

            @Override
            public void run() {
                SSCCE sSCCE = new SSCCE();
            }
        });
    }

    private JPanel getPane() {
        JPanel ret = new JPanel();
        JLabel lbl = new JLabel("This is a pane.");
        ret.add(lbl);
        ret.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
        ret.setBackground(Color.gray);
        return ret;
    }

    public SSCCE() {
        innerPane.setLayout(new GridLayout(0, 1));
        add(scr);
        for (int i = 0; i < 30; i++) {
            innerPane.add(getPane());
        }
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setVisible(true);
        start();
    }

    private void start() {
        timer = new javax.swing.Timer(2000, updateCol());
        timer.start();
        timer.setRepeats(false);
    }

    private Action updateCol() {
        return new AbstractAction("Hide Row Action") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                for (int i = 0; i < 30; i++) {
                    if (i % 2 == 0) {
                        innerPane.getComponent(i).setVisible(false);
                    }
                }
            }
        };
    }
}

答案 3 :(得分:2)

我真的不喜欢GridLayout。我建议你看一下TableLayout,而不是编写自己的布局管理器。我一直都在使用它。

初始学习曲线比GridLayout稍微陡峭,但很容易让它按照您想要的方式运行。

http://java.sun.com/products/jfc/tsc/articles/tablelayout/

答案 4 :(得分:0)

尝试使用BoxLayout与组件对齐。例如:

JPanel innerPane = new JPanel();
BoxLayout innerPaneLayout = new BoxLayout(innerPane,BoxLayout.Y_AXIS);
innerPane.setLayout(innerPaneLayout);

for (int i = 0; i < 30; i++)
{
     JPane newPane = getPane();
     innerPane.add(newPane);
     newPane.setAlignmentY(Component.TOP_ALIGNMENT);
}

for (int i = 0; i < 30; i++)
{
      if (i%2==0)
         innerPane.getComponent(i).setVisible(false);
}

答案 5 :(得分:0)

仅出于答案的完整性,您可以随意将add()remove()组件往返于布局中,以免它们占用空间。

有时候,这可能比处理自定义布局更简单。

我不是Java Swing专家,但是在添加/删除组件时,您可能需要使布局无效/重新验证。方法invalidate()revalidate()repaint()可能有用。