了解布局管理员 - 我不是,请赐教

时间:2011-04-03 18:12:28

标签: java swing

当我调用validate()和repaint()方法时,我只是不明白为什么要调整大小。我很难理解这一点。从本质上讲,我的程序就是这样显示的。我有一个主框架,我插入各种JPanels,我正在扩展我的相册的各种功能。下面的类是NewAlbum类,它应该允许用户选择文件并从中制作新的专辑。

选择文件的代码很好用。选择文件后,对NewAlbum面板的更改应该是选择文件按钮被替换为完成按钮。在完成按钮下面是一个JSplitPane,水平分离器正好偏离中心,右侧比左侧大。左侧最终会有每张照片的缩略图,因为有关照片的元数据会输入到右侧。

右侧窗格是一个带有单个JPanel的JScrollPane,它以网格形式包含要求用户提供数据的4个条目。添加完所有内容之后,尺寸就是我想要的尺寸,但是当我调用验证/重绘组合时,尺寸变得“混乱”。我很确定这是因为我不了解我正在使用或扩展的各种类的默认布局管理器。请帮我理解。另外,告诉我GridBagLayout是否是我想要的,或者我正在寻找的是另一个。

NewAlbum代码如下。

我为无法编译的代码道歉。我想你可以看看课堂并告诉我,“哦,是的,这就是问题所在。”以下是可编译的并且确实证明了问题。选择文件后,拆分窗格窗口太薄太长。我希望它适合框架内部。实际上,它应该适合JFrame内部的JPanel。

谢谢, 安迪

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

class Main extends JFrame {
   static JPanel transientPanel = null;

   public Main() {
      super();
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      setSize(640, 480);
      JMenuBar menuBar = new JMenuBar();
      JMenu menu = new JMenu("Example");
      JMenuItem albumMenu = new JMenuItem("New Album");
      albumMenu.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            transientPanel = new NewAlbum();
            add(transientPanel);
            validate();
            repaint();
         }
      });
      menu.add(albumMenu);
      menuBar.add(menu);

      setJMenuBar(menuBar);
      validate();
   }

   public static void main(String[] args) {
      final Main m = new Main();
      m.setVisible(true);
   }
}
/**
 * @description NewAlbum is the window that is presented to the user
 * to select new photographs for the album.  Once selected, the user
 * will be presented a form, of sorts, to complete the metadata for this
 * album.
 * @author Andy
 */
class NewAlbum extends JPanel {
    JButton selectFiles;
    JButton done;
    JButton nextButton = new JButton("Next Image");

    ArrayList<File> filesArray;

    JSplitPane splitWindow = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
    JScrollPane scrollWindow;
    JPanel rightSidePanel = new JPanel();
    JPanel leftSidePanel = new JPanel();

    JLabel subjectLabel = new JLabel("Image subject:");
    JLabel locationLabel = new JLabel("Image location:");
    JLabel commentLabel = new JLabel("Comments:");
    JLabel dateLabel = new JLabel("Date (mm/dd/yyyy):");

    JTextField subjectText = new JTextField(25);
    JTextField locationText = new JTextField(25);
    JTextArea commentText = new JTextArea(4, 25);
    JTextField dateText = new JTextField(10);

    public NewAlbum() {
        super();

        selectFiles = new JButton("Select Photos");

        selectFiles.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                selectFilesForAlbum();
            }
        });
        add(selectFiles);
    }

    private void configureRightPanel() {
        int jPanelX = getParent().getWidth();
        int jPanelY = getParent().getHeight() - 30; // this should account for buttons

        // now, resize this panel so that it will be the right size for the split pane
        jPanelX = jPanelX - (int)(jPanelX * .31);
        rightSidePanel.setSize(jPanelX, jPanelY);

        rightSidePanel.add(subjectLabel);
        rightSidePanel.add(subjectText);
        rightSidePanel.add(locationLabel);
        rightSidePanel.add(locationText);
        rightSidePanel.add(commentLabel);
        rightSidePanel.add(commentText);
        rightSidePanel.add(dateLabel);
        rightSidePanel.add(dateText);
        rightSidePanel.add(nextButton);

        // iterate over the photos selected, make bogus info for now
    }

    private ArrayList<File> makeFileIntoArrayList(File[] f) {
        ArrayList<File> a = new ArrayList<File>();
        a.addAll(Arrays.asList(f));

        return filesArray = a;
    }

    /**
     * selectFilesForAlbum
     * This method is private to the NewAlbum class.  It is the handler for
     * when the user clicks on the "select photos" button.  When the function
     * executes, it displays the JFileChooser so that the user may select
     * the desired photos.  The files selected are assigned to a class variable
     * of type File[] which is used by the enterPhotoInfo method.
     *
     * @return void
     */
    private void selectFilesForAlbum() {
        JFileChooser jfc = new JFileChooser();
        jfc.setMultiSelectionEnabled(true);
        jfc.showOpenDialog(this);
        makeFileIntoArrayList(jfc.getSelectedFiles());

        changeButtonToDone();
        enterPhotoInfo();

        // TODO write the photo album to the disk
    }

    private void changeButtonToDone() {
        remove(selectFiles);
        done = new JButton("Done");
        add(done);

        // by the time this gets called, we'll have a parent container
        getParent().validate();
        getParent().repaint();
    }

    private void enterPhotoInfo() {
        splitWindow.setSize(this.getWidth(), this.getHeight() - 30);

        // remove when the left side panel actually has something
        Dimension iewDims = splitWindow.getSize();
        int leftX = iewDims.width - (int)(iewDims.width * .69);
        int leftY = iewDims.height;

        leftSidePanel.setSize(leftX, leftY);

        configureRightPanel();

        scrollWindow = new JScrollPane(rightSidePanel);
        scrollWindow.setSize(rightSidePanel.getSize());

        splitWindow.setRightComponent(scrollWindow);
        splitWindow.setLeftComponent(leftSidePanel);
        splitWindow.setDividerLocation(.31);

        System.out.println("Printing dimensions of before validate/repaint: this, splitWindow, scrollWindow, LSP, RSP");
        debugPrintDimensions(this);
        debugPrintDimensions(splitWindow);
        debugPrintDimensions(scrollWindow);
        debugPrintDimensions(leftSidePanel);
        debugPrintDimensions(rightSidePanel);

        //infoEntryWindow.add(infoScroller);
        this.add(splitWindow);
        this.validate();
        this.repaint();

        System.out.println("Printing dimensions of: this, splitWindow, scrollWindow, LSP, RSP");
        debugPrintDimensions(this);
        debugPrintDimensions(splitWindow);
        debugPrintDimensions(scrollWindow);
        debugPrintDimensions(leftSidePanel);
        debugPrintDimensions(rightSidePanel);
    }

    private void debugPrintDimensions(Container c) {
        System.out.println("DEBUG: Containers (x,y): (" +
                String.valueOf(c.getWidth()) +
                "," +
                String.valueOf(c.getHeight()) +
                ")");
    }
}

2 个答案:

答案 0 :(得分:2)

通过选择LayoutManager,您将布局的控制权交给该布局管理器。您可以通过在布局组件上设置首选尺寸,通过布局约束和限制来提供LayoutManager提示,但实质上,布局管理器将进行调用。

使用GridBagLayoutManager,您几乎可以使用约束和组件维度设置来实现任何目标,但要想做到这一点仍然很棘手。尝试在组件上设置首选大小。

我过去常常使用GridBagLayoutManager,但后来我遇到了MigLayout,这在简单的配置和布局一致性方面确实是一个巨大的进步。我建议你试一试。

答案 1 :(得分:2)

  

另外,告诉我GridBagLayout是否是我想要的,或者我正在寻找的是另一个。

您可以为作业使用适当的布局管理器。这也意味着在不同的面板上使用不同的布局管理器。

splitWindow.setSize(this.getWidth(), this.getHeight() - 30); 

你永远不应该使用setSize()。这是布局管理器的工作,用于根据布局管理器的规则确定组件的大小。

所有组件都有一个首选大小,布局管理器使用它。有时您可以使用setPreferredSize()来更改默认值。