Using Java pack() method

2019-01-25 00:47发布

问题:

I can't make the pack() method work. I tried several things. My code looks like this at the moment:

Class 1:

public static void main( String[] args )
    {
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run()
        {
         JavaGui mygui = new JavaGui();
   //       mygui.setSize(1154, 753);
            mygui.setVisible(true);
            mygui.pack();

Class 2:

public class JavaGui extends javax.swing.JFrame 
    {
    public JavaGui()
        {
        getContentPane().setLayout(null);
        ..
        getContentPane().add(panelLeft);
        ...
        getContentPane().add(panelRight);

I tried putting the pack method in everywhere, but it's not going to work with this way of adding gui elements. Any suggestions why? I also tried adding everything to a JFrame instead of the getContentPane(), but I can't make that work either.

回答1:

  1. Don't use null layouts together with pack(). The pack method tells the layout managers and components to size themselves optimally, and if you instead use null layouts, then the gui risks shrinking to a minimal size, since there is no layout to hold it together.
  2. Don't use null layouts at all for the most part. Using these risk your creating rigid GUI's that are almost impossible to extend, improve, debug.
  3. Don't use setSize(...) and pack(). The layouts mostly respect the preferred sizes of components, not their sizes.

Instead:

  1. Use a pleasing and sensible combination of nested JPanels, each using its own layout manager.
  2. Let the components and the layout managers size themselves.
  3. Then pack should help.
  4. The general order that I do is to add all components to the GUI, then call pack(), then setLocationByPlatform(true) (I think), then setVisible(true).

For better help, please check out the Swing Layout Manager Tutorials.

Here are a couple examples to other questions on this site that use various layout managers:

  • A combination of BorderLayout and GridLayout to create a calculator
  • BorderLayout and BoxLayout Combination for labels and JTextFields
  • Using GridBagLayout to create flexible label/textfield grid


回答2:

I would recommened beginners on building up swing guis to use a good ide with a builtin gui designer like eclipse and windowbuilder or netbeans with matisse. It will help you building up a prototype of your desired gui and gives you an insight how the layouting is done in the source code. Experiment with the differenet layouts and what is happening when some values are changed.

one does not simply build up a well behaving gui without understanding how the layout works, so doing the recommended tutorials and looking at examples as already posted by Hovercraft Full Of Eels is absolutely necessary.

For your case i just guess what you were up to. Because youre mentioning left and right panels i suggest a JSplitPane which let you divide your screen in two areas which are customizable in size and orientation.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;

public class JavaGui extends JFrame {

    //SerialVersionId http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it
    private static final long   serialVersionUID    = 1L;

    public static void main(String[] args) {
        //Calls to Gui Code must happen on the event dispatch thread that the gui does not get stuck
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new JavaGui().setVisible(true);
            }
        });
    }       

    public JavaGui() {
        // Set the desired size of the frame to determine the maximum size of its components
        setPreferredSize(new Dimension(1024, 768));

        // Set the default close operation, if press x on frame, destroy the frame and exit the application - others are just destroy the frame or just hide the
        // frame
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // BorderLayout because we just need a centric gui with one component, here JSplitPane in full size
        getContentPane().setLayout(new BorderLayout(0, 0));

        // JsplitPane is a bit special as it depends on the divider location between both panels, for the sake of a small example we take the default -1,
        JSplitPane splitPane = new JSplitPane();
        // 0.5 divides extra space equally to left and right component when resizing the frame - so specifiying a size for the left and right component is not
        // necessary
        // use the divider location default -1 to let the width of the left component decide where the right component begins, in that case because of the
        // resize weight half and half
        splitPane.setDividerLocation(-1);
        splitPane.setResizeWeight(0.5);
        getContentPane().add(splitPane, BorderLayout.CENTER);

        // For the panels the same layout as default as the intention is not stated in your question
        JPanel leftPanel = new JPanel();
        splitPane.setLeftComponent(leftPanel);
        leftPanel.setLayout(new BorderLayout(0, 0));

        JPanel rightPanel = new JPanel();
        splitPane.setRightComponent(rightPanel);
        rightPanel.setLayout(new BorderLayout(0, 0));

        // Add a button Panel to the south for doing something - flow layout for letting the components flow to the right side
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        getContentPane().add(buttonPanel, BorderLayout.SOUTH);

        // Close Button for closing the frame
        JButton btnExit = new JButton("Destroy this frame, but let application run");
        btnExit.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        });
        buttonPanel.add(btnExit);

        // Set every component to its preferred size
        pack();

        // Make it visible
        setVisible(true);
    }
}


回答3:

If you want your JFrame to work with a null layout, rearrange your code so that it looks like this:

public class JavaGui extends javax.swing.JFrame 
{
public JavaGui()
{
    setMinimumSize(1154, 753); // Make sure you do setMinimumSize() instead of setSize() when using pack() so that the JFrame does not shrink to 0 size
    setLayout(null);
    add(panelLeft);
    add(panelRight);
    pack();
}
// Next is main method

Main:

public static void main(String[] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
    public void run()
    {
        new JavaGui().setVisible(true);
        // Do not do any formatting for your JFrame here
    }
});

Before, you were modifying the JFrame after it was set visible, so that usually does not work, except for pack(). All components and settings for your JFrame should not be in the main method if you are using an anonymous inner class.

You can also use other layouts. Null layouts are for getting pixels in precise locations, which is used for advanced GUI design such as creating a custom GUI, but it seems that you are making a generic GUI with JPanels. For this, I would recommend using a GridBagLayout, which keeps everything centered if the frame is resized and is easy to use. To use a GridBagLayout, you have to replace setLayout(null); with setLayout(new GridBagLayout()); and set GridBagConstraints. Here is some example code of making a panel with a component and a GridBagLayout:

JPanel pane = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
if (shouldFill) {
            //natural height, maximum width
            c.fill = GridBagConstraints.HORIZONTAL;
}
//For each component to be added to this container:
//...Create the component...
//...Set instance variables in the GridBagConstraints instance...
pane.add(theComponent, c);
// Source: Oracle Docs