为什么setPreferredSize()有时优先于setMinimumSize()?

时间:2016-08-10 14:07:21

标签: java swing

昨天我发现了一个错误,我在JTable添加了JPanelJButton(其中包含JScrollPane)。 JButton已固定在表格的底部,并在点击时向JTable添加了一行。

问题是如果表格大于JScrollPane,它只允许你滚动到JTable的底部;你再也无法进入JButton了。今天,我做了一个MCVE尝试并获得帮助,但首先我更多地讨论它并最终解决我的问题,但在某种程度上让我有更多的问题而不是答案......这就是MCVE我准备好了:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class MCVE extends JFrame{

    private JButton addRow;
    private MCVEModel tableModel;
    private JTable table;
    private JScrollPane pane;
    private JPanel scrollPanel, panel;

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

    public MCVE() {
        initialize();
    }

    public void initialize () {
        this.setTitle("Halp");
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setBounds(50, 50, 500, 300);
        this.setResizable(false);

        JPanel mainPanel = new JPanel(new GridBagLayout());

        /** The JPanel everything is put into **/
        scrollPanel = new JPanel();
        scrollPanel.setLayout(new BoxLayout(scrollPanel, BoxLayout.Y_AXIS));

        /** The JScrollPane we're using **/
        pane = new JScrollPane(scrollPanel);
        pane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); 
        pane.getVerticalScrollBar().setUnitIncrement(10);

        /** The button which keeps getting cut off.... **/
        addRow = new JButton("...");
        addRow.setBackground(Color.WHITE);
        addRow.setMnemonic('R');
        addRow.setFocusable(false);
        addRow.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                addNewRow();
            }
        });

        /** I wrap the button into this panel so I can affix it to the left **/
        panel = new JPanel();
        panel.setMinimumSize(new Dimension(0, 20));
        panel.setLayout(null);
        addRow.setBounds(0, 0, 35, 15);
        panel.add(addRow);

        /** Faking some data to get the table to populate **/
        ArrayList<List<String>> allData = new ArrayList<List<String>>();
        ArrayList<String> fakeData = new ArrayList<String>();
        fakeData.addAll(Arrays.asList(
                  new String[]{"this", "is", "just", "sample", "data"}));
        for(int i = 0; i < 5; i++)
            allData.add(fakeData);

        List<String> columnNames = Arrays.asList(new String[] {"", "", "", "", ""});
        tableModel = new MCVEModel(columnNames, allData);

        table = new JTable();
        table.setModel(tableModel);

        /** Adding it all together **/
        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1;
        c.weighty = 1;
        scrollPanel.add(table);
        scrollPanel.add(panel);
        mainPanel.add(pane, c);
        this.add(mainPanel);

        this.setVisible(true);
    }

    public void addNewRow () {
        tableModel.addRow(tableModel.getRowCount(), 
                new String[]{"true", "", "", "false", "false"});
        tableModel.fireTableRowsInserted(
                tableModel.getRowCount(), tableModel.getRowCount());
    }
} 
/** 
 * Just here to keep things compilable. Seriously cut back for the MCVE, 
 * but still replicates the problem without any errors.
 * Nothing below here should be relevant to the issue. 
 */
class MCVEModel extends DefaultTableModel {

    private static final long serialVersionUID = -6598574844380686148L;
    private List<String> columnNames;
    private List<List<String>> values;

    public MCVEModel (List<String> columnNames, List<List<String>> strings) {
        this.columnNames = columnNames;
        this.values = strings;
    }

    public int getColumnCount() {
        return columnNames.size();
    }

    public int getRowCount() {
        return values == null || values.size() == 0 ? 0 : values.get(0).size();
    }

    public String getColumnName(int col) {
        return columnNames.get(col);
    }

    public Object getValueAt(int row, int col) {
        return values.get(col).get(row);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Class getColumnClass(int c) {
        return String.class;
    }

    public boolean isCellEditable(int row, int col) {
        return true;
    }

    public void setValueAt(Object value, int row, int col) {
        values.get(col).set(row, (String) value);
        fireTableCellUpdated(row, col);
    }

    public void removeRow(int row) {
        for(int i = 0; i < values.size(); i++)
            values.get(i).remove(row);
        this.fireTableRowsDeleted(row, row);
    }

    public void addRow(int row, String[] strings) {
        for(int i = 0; i < values.size(); i++)
            values.get(i).add(row, strings[i]);
        fireTableRowsInserted(row, row);
    }
}

问题在于这一行:

panel.setMinimumSize(new Dimension(0, 20));

更准确地说,它是&#34;最小&#34;。通过将其更改为:

panel.setPreferredSize(new Dimension(0, 20));

我完全掌握了我需要的功能。现在当表格对JScrollPanel来说太大时,我们仍然可以向下滚动并查看JButton;它不再被切断了。

我假设这意味着JPanel的父母没有遵守其最小尺寸,但确实尊重其首选尺寸。为什么是这样?我原以为setPreferredSize()setMinimumSize()setMaximumSize()互动了,#34;我更喜欢这么大,但不管我能做什么都不是小于我的最小值或大于我的最大值,&#34;但似乎事实并非如此。我知道这些方法都不应该过于频繁使用,但我应该何时使用setMinimumSize()而不是setPreferredSize(),反之亦然?

1 个答案:

答案 0 :(得分:3)

神奇就是这条线:

scrollPanel.add(panel);

因此,scrollPanel将包含此面板。然后,JScrollPane尊重preferredSize - s。这是有道理的,因为它的目的是通过使用滚动条为所包含的组件留出足够的空间。换句话说,JScrollPane -s insmentation忽略了minimumSize - s。

<强>更新

从另一个角度来看,JScrollPane -s源代码检查其子项的preferredSize,但不检查minimumSize。这里没有深刻的哲学,JScrollPane就是这样实现的。