不可见的组件仍然占用空间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  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  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; } } 

截图

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

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

有没有快速简便的方法来做到这一点?

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

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

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

编辑:

 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(); } } 

这有三种方法可以摆脱我的困扰。

隐藏组件

 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的大多数用户而言,这将更加直观,并且具有相同的最终效果。

可见:

禁用组件

 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); } }); } } 

不要调用Thread.sleep(int); 在EDT期间,因为阻止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); } } } }; } } 

我真的不喜欢GridLayout。 我建议你看一下TableLayout,而不是编写自己的布局管理器。 我用它所有的时间。

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

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

尝试使用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); }