2013-12-10 172 views
2

我發現自己寫了不少最近的程序,都需要顯示一些數據的集合。到目前爲止,我認爲最好的方法是創建包含集合中每個項目數據的小JPanel,並將它們全部放入一個大的JPanel中,然後放入一個JScrollPane中。它的工作原理和看起來一樣,但有一個問題:我似乎無法讓較小的JPanel從較大的JPanel頂部開始。在列表中組織JPanels的最佳方式是什麼?

Example

問題是唯一明顯的,當我已經有了少量的小JPanels(綠色)加入到更大的JPanel(紅色)。

下面描述的是我用於生產上述的方法,我想知道是否有更好的方法我能做到這一點(在列表從頂部開始像它應該):

  • 我創建了一個擴展JPanel的類,並在其中添加了我想要顯示的所有數據。我們將它稱爲「SmallPanel.java」。我沒有設置它的大小(稍後)。

  • 在我的主窗口的類(它擴展的JFrame):

    private JScrollPane scrollPane; 
    private JPanel panel; 
    ... 
    scrollPane = new JScrollPane(); 
    getContentPane().add(scrollPane); 
    
    panel = new JPanel(); 
    panel.setLayout(new GridBagLayout()); 
    scrollPane.setViewportView(panel); 
    ... 
    private void addPanel() 
    { 
        GridBagConstraints gbc = new GridBagConstraints(); 
        gbc.gridx = 0; 
        gbc.gridy = panel.getComponentCount(); //The new JPanel's place in the list 
        gbc.fill = GridBagConstraints.HORIZONTAL; 
        gbc.anchor = GridBagConstraints.PAGE_START; //I thought this would do it 
        gbc.ipady = 130; //Set the panel's height, the width will get set to that of the container JPanel (which is what I want since I'd like my JFrames to be resizable) 
        gbc.insets = new Insets(2, 0, 2, 0); //Separation between JPanels in the list 
        gbc.weightx = 1.0; 
        SmallPanel smallPanel = new SmallPanel(); 
        panel.add(smallPanel, gbc); 
        panel.revalidate(); 
        panel.invalidate(); 
        panel.repaint(); //Better safe than peeved 
    } 
    
  • 調用addPanel()方法,我想添加一個面板每次。

編輯

最終解決方案(基於以下MadProgrammer的答案):

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.Insets; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Random; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.border.BevelBorder; 

public class ListPanel extends JPanel 
{ 
    private static final long serialVersionUID = 1L; 
    private JPanel fillerPanel; 
    private ArrayList<JPanel> panels; 

    public ListPanel(List<JPanel> panels, int height) 
    { 
     this(panels, height, new Insets(2, 0, 2, 0)); 
    } 

    public ListPanel(List<JPanel> panels, int height, Insets insets) 
    { 
     this(); 
     for (JPanel panel : panels) 
      addPanel(panel, height, insets); 
    } 

    public ListPanel() 
    { 
     super(); 
     this.fillerPanel = new JPanel(); 
     this.fillerPanel.setMinimumSize(new Dimension(0, 0)); 
     this.panels = new ArrayList<JPanel>(); 
     setLayout(new GridBagLayout()); 
    } 

    public void addPanel(JPanel p, int height) 
    { 
     addPanel(p, height, new Insets(2, 0, 2, 0)); 
    } 

    public void addPanel(JPanel p, int height, Insets insets) 
    { 
     super.remove(fillerPanel); 
     GridBagConstraints gbc = new GridBagConstraints(); 
     gbc.gridx = 0; 
     gbc.gridy = getComponentCount(); 
     gbc.fill = GridBagConstraints.HORIZONTAL; 
     gbc.anchor = GridBagConstraints.PAGE_START; 
     gbc.ipady = height; 
     gbc.insets = insets; 
     gbc.weightx = 1.0; 
     panels.add(p); 
     add(p, gbc); 
     gbc = new GridBagConstraints(); 
     gbc.gridx = 0; 
     gbc.gridy = getComponentCount(); 
     gbc.fill = GridBagConstraints.VERTICAL; 
     gbc.weighty = 1.0; 
     add(fillerPanel, gbc); 
     revalidate(); 
     invalidate(); 
     repaint(); 
    } 

    public void removePanel(JPanel p) 
    { 
     removePanel(panels.indexOf(p)); 
    } 

    public void removePanel(int i) 
    { 
     super.remove(i); 
     panels.remove(i); 
     revalidate(); 
     invalidate(); 
     repaint(); 
    } 

    public ArrayList<JPanel> getPanels() 
    { 
     return this.panels; 
    } 

    public static void main(String[] args) 
    { 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setMinimumSize(new Dimension(500, 500)); 
     f.setLocationRelativeTo(null); 
     f.getContentPane().setLayout(new BorderLayout()); 
     final ListPanel listPanel = new ListPanel(); 
     for (int i = 1; i <= 10; i++) 
      listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50); 
     JButton btnAdd = new JButton("Add"); 
     btnAdd.addActionListener(new ActionListener() 
     { 
      public void actionPerformed(ActionEvent paramActionEvent) 
      { 
       listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50); 
      } 
     }); 
     JButton btnRemove = new JButton("Remove"); 
     btnRemove.addActionListener(new ActionListener() 
     { 
      public void actionPerformed(ActionEvent paramActionEvent) 
      { 
       listPanel.removePanel(0); 
      } 
     }); 
     f.getContentPane().add(btnRemove, BorderLayout.NORTH); 
     f.getContentPane().add(btnAdd, BorderLayout.SOUTH); 
     JScrollPane scrollPane = new JScrollPane(); 
     scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 
     scrollPane.setViewportView(listPanel); 
     f.getContentPane().add(scrollPane, BorderLayout.CENTER); 
     f.setVisible(true); 
    } 

    public static JPanel getRandomJPanel() 
    { 
     JPanel panel = new JPanel(); 
     panel.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null)); 
     panel.add(new JLabel("This is a randomly sized JPanel")); 
     panel.setBackground(new Color(new Random().nextFloat(), new Random().nextFloat(), new Random().nextFloat())); 
     return panel; 
    } 
} 
+0

爲了更好地幫助越早,張貼[SSCCE(http://sscce.org/)。 –

+0

@AndrewThompson我認爲這對於SSCCE來說太籠統了,但我盡力了(嘿,我得到了我需要的答案)。 –

回答

2

我已經找到了最好的解決方案是使用VerticalLayout,從SwingLabs SwingX(可從here下載)庫。

你「可以」使用GridBagLayout與定位在年底的無形的組成部分,其weighty屬性設置爲1,但是這是一個很大的額外的工作來管理,因爲你需要不斷更新的X/Y位置所有的組件,以保持它在的地方......

更新與GridBagLayout例如

enter image description here

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.Insets; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 

public class VerticalLayoutExample { 

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

    public VerticalLayoutExample() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
       } 

       final TestPane pane = new TestPane(); 
       JButton add = new JButton("Add"); 
       add.addActionListener(new ActionListener() { 
        @Override 
        public void actionPerformed(ActionEvent e) { 
         pane.addAnotherPane(); 
        } 
       }); 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new BorderLayout()); 
       frame.add(new JScrollPane(pane)); 
       frame.add(add, BorderLayout.SOUTH); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class TestPane extends JPanel { 

     private JPanel filler; 
     private int y = 0; 

     public TestPane() { 

      setBackground(Color.RED); 
      setLayout(new GridBagLayout()); 

      filler = new JPanel(); 
      filler.setOpaque(false); 
      GridBagConstraints gbc = new GridBagConstraints(); 
      gbc.weighty = 1; 
      gbc.gridy = 0; 

      add(filler, gbc); 

     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(200, 400); 
     } 

     public void addAnotherPane() { 

      JPanel panel = new JPanel(new GridBagLayout()); 
      panel.add(new JLabel("Hello")); 

      GridBagConstraints gbc = new GridBagConstraints(); 
      gbc.gridy = y++; 
      gbc.weightx = 1; 
      gbc.fill = GridBagConstraints.HORIZONTAL; 
      gbc.gridwidth = GridBagConstraints.REMAINDER; 
      gbc.insets = new Insets(4, 4, 4, 4); 

      add(panel, gbc); 

      GridBagLayout gbl = ((GridBagLayout)getLayout()); 
      gbc = gbl.getConstraints(filler); 
      gbc.gridy = y++; 
      gbl.setConstraints(filler, gbc); 

      revalidate(); 
      repaint(); 

     }   
    }   
} 

這只是一個概念。作爲camickr所指出的,只要你知道的最後一個組件,您可以調整該組件的GridBagConstraints,使最後的組分,它是在列表中有1weighty,而不是...

就可以了,您可以覆蓋一些GridBagLayout做,例如,而不是使用面板的首選大小的東西,我問GridBagLayout,使其填補了HORIZONTAL寬度父容器的...

+0

嗯,如果可以的話,我想避免使用lib(特別是考慮我製作的程序非常小),但我一定會牢記這一點。 你介意擴展你提出的第二個想法嗎?我認爲這更符合我期待的內容。 –

+0

+1,@MeshulamSilk,閱讀Swing教程中關於[如何使用Grid Bag佈局]的部分(http://docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html)。它解釋了weightx/y約束是如何工作的。我不確定你需要添加一個不可見的組件到最後。我認爲您可以對所有組件使用1的重量值,但像BoxLayout一樣,只要確保面板的最大尺寸等於其首選尺寸即可。 (雖然我可能會錯,因爲我很少使用GridBagLayout)。 – camickr

+0

但是不會設置最大尺寸會導致調整窗口大小的問題?高度必須是靜態的,但寬度是動態的。在我目前的方法中,我從來沒有設置任何尺寸,高度來自'gbc.ipady = 130;'。 –

2

可以使用垂直BoxLayout

只要確保面板的最大尺寸等於首選尺寸,面板不會增長。

編輯:

由於類已經有一個自定義面板,所有你需要做的是重寫調用getMaximumSize()方法返回一個適當的值。喜歡的東西:

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

public class VerticalLayoutExample2 { 

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

    public VerticalLayoutExample2() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
       } 

       final TestPane pane = new TestPane(); 
       JButton add = new JButton("Add"); 
       add.addActionListener(new ActionListener() { 
        @Override 
        public void actionPerformed(ActionEvent e) { 
         pane.addAnotherPane(); 
        } 
       }); 

       JFrame frame = new JFrame("Testing"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new BorderLayout()); 
       frame.add(new JScrollPane(pane)); 
       frame.add(add, BorderLayout.SOUTH); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class TestPane extends JPanel { 

     private JPanel filler; 
     private int y = 0; 

     public TestPane() { 

      setBackground(Color.RED); 
      setLayout(new GridBagLayout()); 
      setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 
      setBorder(new EmptyBorder(4, 4, 4, 4)); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(200, 400); 
     } 

     public void addAnotherPane() { 
      SmallPanel panel = new SmallPanel(); 
      panel.setLayout(new GridBagLayout()); 
      panel.add(new JLabel("Hello")); 
      add(panel); 
      add(Box.createVerticalStrut(4)); 

      revalidate(); 
      repaint(); 
     } 
    } 

    static class SmallPanel extends JPanel 
    { 
     @Override 
     public Dimension getMaximumSize() 
     { 
      Dimension preferred = super.getPreferredSize(); 
      Dimension maximum = super.getMaximumSize(); 
      maximum.height = preferred.height; 
      return maximum; 
     } 
    } 
} 

我知道你提到你不想使用一個lib,但你也可以看看Relative Layout。它只是一個班級。它可以輕鬆模仿BoxLayout,但更易於使用,因爲您無需重寫getMaximumSize()方法或將Box組件添加到面板以提供垂直間距。

你將它設置爲面板的佈局如下:

RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS); 
rl.setFill(true); // fills components horizontally 
rl.setGap(4); // vertical gap between panels 
yourPanel.setLayout(rl); 
yourPanel.add(new SmallPanel(...)); 
yourPanel.add(new SmallPanel(...)); 
+0

我一定會考慮它。謝謝! –

+0

@MeshulamSilk,見編輯。 – camickr

+0

我其實不得不去嘗試一下,直到後來才試用,但是感謝,代碼讓生活更輕鬆。 –

相關問題