简体   繁体   English

添加组件时ScrollPane扩展

[英]ScrollPane expanding when components added

I've spent a while creating an sscce from my larger program, I hope it's small enough! 我花了一段时间从我的大型程序中创建一个sscce,我希望它足够小!

I've a JSplitPane with a table on top, and below is a JPanel. 我有一个顶部有桌子的JSplitPane,下面是一个JPanel。

The bottom panel contains smaller JPanels, or 'entries'. 底部面板包含较小的JPanel或“条目”。 As the number of entries grows, the bottom SplitPane takes up the space of the top pane. 随着条目数量的增加,底部的SplitPane占用顶部窗格的空间。

Dimension dim = getPreferredSize();        
setPreferredSize(dim);

In the first class, uncommenting this code solves the problem, but I've no idea why. 在第一课中,取消注释此代码可以解决问题,但我不知道为什么。 I'd like to understand how to deal with resizing better if anyone can help? 如果有人可以提供帮助,我想了解如何更好地处理调整大小问题? (I've also got to deal with the text area expanding horizontally, which is one of the reasons some of the constraints might look a little odd) (我还要处理水平扩展的文本区域,这是一些限制可能看起来有点奇怪的原因之一)

Here's the bottom panel: 这是底部面板:

package example;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.border.Border;

public class BottomPanel extends JPanel{
    private JLabel summary = new JLabel();
    private EntryPanel entryPanel = new EntryPanel(); 

    public BottomPanel() {
        //Dimension dim = getPreferredSize();
        //setPreferredSize(dim);
        setLayout(new GridBagLayout());
        entryPanel.setBorder(BorderFactory.createLineBorder(Color.RED));
        setComponents();
    }

    private void setComponents() {
        Border test = BorderFactory.createLineBorder(Color.BLUE);
        JScrollPane jsp = new JScrollPane(entryPanel);
        JPanel dummy1 = new JPanel();
        JPanel dummy2 = new JPanel();
        JPanel dummy3 = new JPanel();
        dummy1.setBorder(test);
        dummy2.setBorder(test);
        dummy3.setBorder(test);

        int row = 0;
        add(dummy1, new GBC(0,row).setWeight(0,0));
        // new row
        row++;
        add(summary, new GBC(1,row).setAnchor(GBC.LINE_START));   
        // new row
        row++;
        add(jsp, new GBC(1,row).setWeight(50,70).setFill(GBC.BOTH));
        // new row
        row++;
        add(dummy2, new GBC(2,row).setWeight(0,0));
    }

    private class EntryPanel extends JPanel{
        private List<JPanel> entries = new ArrayList<>();
        private int row = 0;

        private EntryPanel(){
            setLayout(new GridBagLayout());
            for(int x = 0; x < 20; x++)
                createEntryItem("TEST", "test text");
        }

        private void createEntryItem(String s, String text){
            Border test = BorderFactory.createLineBorder(Color.GREEN);
            JPanel entryItem = new JPanel();
            entryItem.setBorder(test);
            entryItem.setLayout(new GridBagLayout());
            JLabel title = new JLabel(s);
            JTextArea details = new JTextArea(text);  

            entryItem.add(title, new GBC(0,0).setAnchor(GBC.LINE_START));
            entryItem.add(details, new GBC(0,1).setAnchor(GBC.LINE_START));
            entryItem.add(new JPanel(), new GBC(1,0).setWeight(100,0));
            entries.add(entryItem);
            displayEntry(entryItem);
        }

        private void displayEntry(JPanel entry){
            JPanel dummy = new JPanel();
            add(entry, new                         GBC(0,row).setAnchor(GBC.LINE_START).setFill(GBC.HORIZONTAL));
            add(dummy, new GBC(1,row).setWeight(100, 0));
            row++;
        }
    }
}

and the containing frame and panel: 包含框架和面板:

package example;
import java.awt.*;
import javax.swing.*;

public class Example extends JFrame{
    private GraphicsDevice gd =     GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();     
    private int width = (int)((gd.getDisplayMode().getWidth())*0.5);
    private int height = (int)((gd.getDisplayMode().getHeight())*0.75);
    private JPanel mainPanel = new JPanel();
    private JSplitPane sp;
    private JTable table = new JTable();
    private BottomPanel bp = new BottomPanel();

    public Example () {
        setSize(width,height);
        setLayout(new BorderLayout());
        sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,new JScrollPane(table), bp);
        add(sp);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    public static void main(String[] args){
        SwingUtilities.invokeLater(new Runnable(){
            public void run(){
                Example ex = new Example();
            }
        });
    } 
}

and the GridBagHelper: 和GridBagHelper:

package example;
import java.awt.GridBagConstraints;

public class GBC extends GridBagConstraints{  
   public GBC(int gridx, int gridy){
      this.gridx = gridx;
      this.gridy = gridy;
   }

   public GBC setAnchor(int anchor){
      this.anchor = anchor;
      return this;
   }

   public GBC setFill(int fill){
      this.fill = fill;
      return this;
   }

   public GBC setWeight(double weightx, double weighty){
      this.weightx = weightx;
      this.weighty = weighty;
      return this;
   }
}

Any help or advice would be really appreciated - I'm self taught and am struggling a fair bit! 任何帮助或建议都会非常感激 - 我是自学成才并且正在努力奋斗!

Thanks 谢谢

I'm too tired to write a proper answer, but I'm glancing through this and one of the first issues I see is setSize(width,height) inside the constructor for Example . 我太累了,无法写出正确的答案,但我正在浏览这个问题,我看到的第一个问题之一就是Example的构造函数中的setSize(width,height) It's better to call pack() on the JFrame and let it size everything itself automatically. 最好在JFrame上调用pack()并让它自动调整大小。

I think pack can be a bit unpredictable with a JScrollPane , though. 不过,我认为使用JScrollPane pack可能有点不可预测。 From memory, I've had it do some weird things like collapse the scroll pane to its minimum size. 从内存中,我已经做了一些奇怪的事情,比如将滚动窗格折叠到最小尺寸。

Using pack on your code example seems to attempt to size it around the sum of the panels in BottomPanel . 在代码示例中使用pack似乎试图围绕BottomPanel面板的总和来BottomPanel (The sum would be taller than my screen resolution, though, so I can't tell what the exact behavior is. It caps the height of the window at the height of my screen.) (总和会高于我的屏幕分辨率,所以我无法确定具体行为是什么。它会在屏幕高度处限制窗口的高度。)

A more elegant way to do what you're trying to do is like this: 一个更优雅的方式去做你想做的事情是这样的:

public Example () {
    // setSize(width,height);
    // setLayout(new BorderLayout());
    sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,new JScrollPane(table), bp);
    sp.setResizeWeight(2.0 / 3.0);
    sp.setPreferredSize(width, height);
    setContentPane(sp);
    // add(sp);
    pack();
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    setVisible(true);
}

I used setResizeWeight to achieve the split pane proportions and set the preferred size of the pane instead of the window. 我使用setResizeWeight来实现拆分窗格比例,并设置窗格的首选大小而不是窗口。

This is pretty similar to the result that you like but it's a little more proper. 这与您喜欢的结果非常相似,但它更合适一些。 It could be that one of the more experienced Swing users here knows a better way to use JScrollPane which eg doesn't involve an explicit setPreferredSize . 可能是这里更有经验的Swing用户之一知道使用JScrollPane的更好方法,例如不涉及显式setPreferredSize

I guess my question is why getting the preferredSize, and then setting the preferredSize makes a difference? 我想我的问题是为什么获得preferredSize,然后设置preferredSize会有所不同?

If we dig down through the code, you'll find... 如果我们深入挖掘代码,你会发现...

public void setPreferredSize(Dimension preferredSize) {
    Dimension old;
    // If the preferred size was set, use it as the old value, otherwise
    // use null to indicate we didn't previously have a set preferred
    // size.
    if (prefSizeSet) {
        old = this.prefSize;
    }
    else {
        old = null;
    }
    this.prefSize = preferredSize;
    prefSizeSet = (preferredSize != null);
    firePropertyChange("preferredSize", old, preferredSize);
}

You can the have a look at how getPreferredSize works... 你可以看看getPreferredSize如何工作......

public boolean isPreferredSizeSet() {
    return prefSizeSet;
}


/**
 * Gets the preferred size of this component.
 * @return a dimension object indicating this component's preferred size
 * @see #getMinimumSize
 * @see LayoutManager
 */
public Dimension getPreferredSize() {
    return preferredSize();
}


/**
 * @deprecated As of JDK version 1.1,
 * replaced by <code>getPreferredSize()</code>.
 */
@Deprecated
public Dimension preferredSize() {
    /* Avoid grabbing the lock if a reasonable cached size value
     * is available.
     */
    Dimension dim = prefSize;
    if (dim == null || !(isPreferredSizeSet() || isValid())) {
        synchronized (getTreeLock()) {
            prefSize = (peer != null) ?
                peer.getPreferredSize() :
                getMinimumSize();
            dim = prefSize;
        }
    }
    return new Dimension(dim);
}

This will return the user set preferredSize if it was set, over calculating its own. 如果已设置,则将返回用户设置的preferredSize,而不是计算自己的设置。

surely i'm assigning a value that was already in place? 我当然要分配一个已经存在的价值?

But you call setPreferredSize before any components are added to the container 但是在将任何组件添加到容器之前调用setPreferredSize

If you change the code to something like... 如果您将代码更改为类似...

Dimension dim = getPreferredSize();
System.out.println(dim);
setPreferredSize(dim);

it will print out something like java.awt.Dimension[width=10,height=10] , so, yeah, small... 它会打印出类似java.awt.Dimension[width=10,height=10] ,所以,是的,小...

But, if you use something like... 但是,如果你使用像......

public BottomPanel() {
    setLayout(new GridBagLayout());
    entryPanel.setBorder(BorderFactory.createLineBorder(Color.RED));
    setComponents();
    Dimension dim = getPreferredSize();
    System.out.println(dim);
    setPreferredSize(dim);
}

it prints out java.awt.Dimension[width=105,height=710] 它打印出java.awt.Dimension[width=105,height=710]

The basic moral to the story is, don't call setPreferred/Minimum/MaximumSize unless you have a very, very good reason to and even then, consider overriding getPreferredSize instead. 故事的基本道德是,不要调用setPreferred/Minimum/MaximumSize除非你有一个非常非常好的理由,甚至可以考虑覆盖getPreferredSize It's no one else's responsibility to calculate the size of the component, but the componet itself 计算组件的大小并不是别人的责任,而是组件本身

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM