MigLayout confused by JTable objects contained in

2019-02-27 10:29发布

问题:

It appears the MigLayout becomes confused by JTable objects contained in JScrollPane objects. Since it seems likely that it is a common idiom to have a JTable within a JScrollPane, I thought it would be important to find out why.

In the attached example program, a simplified form is created, consisting of 12 rows and 4 columns. All widths and heights are set as percentages. A number of components are added to the form, all by cell position. Some span rows or columns. The last column shows cell row index and size percentage.

If the program is started with argument "JLabel", a JLabel contained in a JSCrollPane is added to column 0, all alignments look good. If the program is started with argument "JTable", row sizes for all columns are disrupted.

Is this a bug or a feature?

Make:  javac -classpath <path to miglayout> migtest.java
Usage:  java -classpath <path to miglayout> migtest <JTable|JLabel>

Note: if the code below appears obfuscated (no tabs), I tried various paste methods - I couldn't get the preview to look right. Let me know if there's anything I can do to fix it.

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
import java.io.*;
import java.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.filechooser.*;
import javax.swing.table.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableColumn;
import net.miginfocom.swing.MigLayout;

public class Migtest extends JFrame {
    public final static int appWidth = 1200; // default

    public final static int appHeight = 800; // default

    String[] clientColNames = { "Name", "Address", "Alternate Address" };

    JComponent createClientsPane(int numCols, String arg) {
        if (arg.equals("jtable")) {
            JTable clientsTable = new JTable(1, numCols);
            clientsTable.getTableHeader().setReorderingAllowed(false);
            clientsTable.getSelectionModel().setSelectionMode(
                    ListSelectionModel.SINGLE_SELECTION);
            clientsTable.getSelectionModel().setSelectionInterval(0, 0);
            JScrollPane scrollPane = new JScrollPane(clientsTable);
            return scrollPane;
        } else
            return new JScrollPane(new JLabel("bear tenders"));
    }

    void init(String[] args) {
        JPanel topPanel = null, subpanel = null;
        String arg;
        if (args.length != 1) {
            System.out.println("missing arg - s/b 'JLabel' or 'jTable'");
            System.exit(0);
        }
        arg = args[0].trim().toLowerCase();
        if (!arg.equals("jlabel") && !arg.equals("jtable")) {
            System.out.println("missing arg - s/b 'JLabel' or 'jTable'");
            System.exit(0);
        }
        setSize(appWidth, appHeight);
        topPanel = new JPanel(new MigLayout("fill,debug",
                "[30%][15%][50%][5%]",
                "[5%][7%][5%][10%][7%][7%][7%][7%][6%][7%][2%][30%]"));
        JCheckBox dynamicInstrumentCheckbox = new JCheckBox(
                "Wood Activities Enabled", false);
        topPanel.add(dynamicInstrumentCheckbox, "cell 0 2");
        topPanel.add(new JLabel("BEAR TENDERS"), "cell 0 4, alignx center");
        topPanel.add(createClientsPane(clientColNames.length, arg),
                "cell 0 5, spany 4, grow");
        topPanel.add(new JLabel("BEAR FACTS"), "cell 1 4, alignx center");
        JRadioButton noneButton = new JRadioButton("None");
        topPanel.add(noneButton, "cell 1 5, gapleft 2%");
        JRadioButton rangeButton = new JRadioButton("Fact 1");
        topPanel.add(rangeButton, "cell 1 6, gapleft 2%");
        JRadioButton playbackButton = new JRadioButton("Fact 2");
        topPanel.add(playbackButton, "cell 1 7, gapleft 2%");
        JButton controlsButton = new JButton("Controls =>");
        topPanel.add(controlsButton, "cell 1 8, alignx center");
        topPanel.add(new JLabel("GUMMY BEARS"), "cell 2 1, alignx center");
        topPanel.add(new JLabel("(gummy bears)"), "cell 2 2, spany 4, grow");
        topPanel.add(new JLabel("CHICAGO BEARS"), "cell 2 6, alignx center");
        topPanel.add(new JLabel("(chicago bears)"), "cell 2 7, spany 3, grow");
        topPanel.add(new JLabel("LOG"), "cell 0 10, alignx left");
        JButton clearLogButton = new JButton("Clear Log");
        topPanel.add(clearLogButton, "cell 2 10, alignx right");
        topPanel.add(new JLabel("(log pane)"), "cell 0 11, spanx 3, grow");
        topPanel.add(new JLabel("0-5%"), "cell 3 0, grow");
        topPanel.add(new JLabel("1-7%"), "cell 3 1, grow");
        topPanel.add(new JLabel("2-5%"), "cell 3 2, grow");
        topPanel.add(new JLabel("3-10%"), "cell 3 3, grow");
        topPanel.add(new JLabel("4-7%"), "cell 3 4, grow");
        topPanel.add(new JLabel("5-7%"), "cell 3 5, grow");
        topPanel.add(new JLabel("6-7%"), "cell 3 6, grow");
        topPanel.add(new JLabel("7-7%"), "cell 3 7, grow");
        topPanel.add(new JLabel("8-6%"), "cell 3 8, grow");
        topPanel.add(new JLabel("9-7%"), "cell 3 9, grow");
        topPanel.add(new JLabel("10-2%"), "cell 3 10, grow");
        topPanel.add(new JLabel("11-30%"), "cell 3 11, grow");
        setContentPane(topPanel);
    }

    public static void main(String[] args) {
        try {
            Migtest thisTest = new Migtest();
            thisText.init(args);
//            thisTest.init(new String[] {"jLabel"});
//            thisTest.init(new String[] {"jTable"});

            // center app window
            GraphicsConfiguration gc = thisTest.getGraphicsConfiguration();
            Rectangle bounds = gc.getBounds();
            thisTest.setLocation(
                    (int) ((bounds.width - thisTest.appWidth) / 2),
                    (int) ((bounds.height - thisTest.appHeight) / 2));
            thisTest.setVisible(true);
        } catch (Exception e) {
            System.out.println("runTest caught exception:  " + e.getMessage());
            e.printStackTrace();
        }
    }
} // class test

回答1:

That's a lot of component constraints (too many for me to really digging into them ;-) So just a cursory type of answer:

The difference between a label vs. a table is that the latter implements scrollable, in particular its

Dimension getPreferredScrollableViewportSize();

This is implemented to return a fixed dimension, unfortunately hard-coded in the table's init code:

setPreferredScrollableViewportSize(new Dimension(450, 400));

which is rarely what you want.

The effect on MigLayout is that with the rather large area (and indendent on the actual rows) overflows its cell so much that the other cells must shrink.

The way out in your context is to simply set the prefViewport to null:

clientsTable.setPreferredScrollableViewportSize(null);

(Edit note that the emphasis is on your context - different LayoutManagers will be confused, see a recent question)

A more general solution might be to implement the getter to to something more reasonable, f.i. calculate the preference in terms of #of rows (roughly, though not exactly, what JXTable does)

@Override 
public Dimension getPreferredScrollableViewportSize() {
    if (preferredViewportSize == null) { // hasn't been hard-coded
        if (visibleRowCount < 0) { // a property similar as in JList
             return getPreferredSize(); // use the actual size
        } else {
             Dimension pref = getPreferredSize();
           // calculate height based on # rows that should be visible 
             pref.height = ....
             return pref;
        } 
    } 
    return super.getPr...
}


回答2:

I wonder though if the layout manager isn't handling the component as well as it might. Overall layout row spacing isn't adversely affected unless the scrolled JTable component is added with a "spany" term. Also, by jiggering the layout of my application around just a bit I have found an arrangement where everything aligns okay. And my application has a couple of other scrolled JTable components in the dialog that I left out of the example. So sometimes the layout manager seems to be able to get the spacing right. But I'm now probably going beyond the scope of stackoverflow. (I'd planned to submit the question to miglayout.com, but I haven't been able to register with their support forums.) Thanks for your help.