Force size restrictions on Applet embedded in html

2019-08-11 18:12发布

问题:

I have a Java Applet with a GridLayout containing widgets which I wish to be square, and remain tightly packed to each other (so their sizes are unrestricted).
However, I wish for the GridLayout to take up as much space as possible before being too large for the screen or unable to preserve widget 'squareness'.
Note that the number of rows and columns in the GridLayout are not necessarily equal (the Grid as a whole can be non-square)

This Applet is displayed via this html file;

<html>
<body>
<applet code=client.Grid.class 
        archive="program.jar"
        width=100% height=95%>
</applet>
</body>
</html>

Currently, this makes the Applet expand into the window it is put in; the Grid can be resized by resizing the window, but this causes the geometry of each widget to be changed (losing 'squaredness').

So; where and how do I place these geometrical restrictions?
It can't be in the html file alone, since it has no knowledge of row/column count, and so doesn't know the best size to make the Applet.
However, I don't know how to set the size on the GridLayout or the Panel containing it, since it must know the viewing-browser's page size (to make it as large as possible) and I'm of the impression that the html specified geometry overrides the Applet specified.

EDIT:
Attempting to implement Andrew's suggestion;

screen = new JPanel(new GridLayout(rows, columns)) {

    public Dimension getPreferredSize() {

        Dimension expected = super.getPreferredSize();  
        // calculate preferred size using expected, rows, columns
        return new Dimension(100, 100) // testing
    }
    public Dimension getSize() {
        return getPreferredSize();
    }
};

I understand this ignores the 'minimum size' stuff, but that doesn't matter at the moment.
Screen is placed in the center of a border layout, containing other widgets

getContentPane().add(screen, BorderLayout.CENTER);
getContentPane().add(otherWidgets, BorderLayout.PAGE_END);

I know this doesn't make screen centered in the space it has, but that's not entirely necessary at the moment so I want to keep things as simple as possible.

This isn't at all working; there's no visible difference from what I had before (when viewed through Eclipse; I haven't even reached the html stage yet) excepting the minimum size stuff. The screen component is still being re-sized by the applet at leisure, making the cells 'unsquare'. What am I doing wrong?

回答1:

Put the grid layout container into a grid bag layout as the only component with no constraint, as seen in this answer. That will center it.

Update

And of course, put it in a component that returns a preferred size equating to the maximum square size it can manage depending on the parent size. Such as in SquarePanel.

import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

/**
 * A square panel for rendering. NOTE: To work correctly, this must be the only
 * component in a parent with a layout that allows the child to decide the size.
 */
class SquarePanel extends JPanel {

    @Override
    public Dimension getPreferredSize() {
        Dimension d = super.getPreferredSize();
        System.out.println("Preferred Size: " + d);
        int w = (int) d.getWidth();
        int h = (int) d.getHeight();
        // Set s to the larger of the mimimum component width or height
        int s = (w > h ? w : h);
        Container c = getParent();
        if (c != null ){
            Dimension sz = c.getSize();
            if ( d.getWidth()<sz.getWidth() ) {
                // Increase w to the size available in the parent container
                w = (int)sz.getWidth();
                System.out.println("WxH: " + w + "x" + h);
                // recalculate s
                s = (w < h ? w : h);
            }
            if ( d.getHeight()<sz.getHeight()) {
                // Increase h to the size available in the parent container
                h = (int)sz.getHeight();
                System.out.println("WxH: " + w + "x" + h);
                // recalculate s
                s = (w < h ? w : h);
            }
        }
        // Use s as the basis of a square of side length s.
        System.out.println("Square Preferred Size: " + new Dimension(s, s));
        return new Dimension(s, s);
    }

    @Override
    public Dimension getMinimumSize() {
        return getPreferredSize();
    }

    @Override
    public Dimension getSize() {
        return getPreferredSize();
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                // the GUI as seen by the user (without frame)
                // A single component added to a GBL with no constraint
                // will be centered.
                JPanel gui = new JPanel(new GridBagLayout());
                gui.setBackground(Color.BLUE);

                SquarePanel p = new SquarePanel();
                p.setBorder(new EmptyBorder(5,15,5,15));
                p.setLayout(new GridLayout(3,0,2,2));
                for (int ii=1; ii<13; ii++) {
                    p.add(new JButton("" + ii));
                }
                p.setBackground(Color.red);
                gui.add(p);

                JFrame f = new JFrame("Demo");
                f.add(gui);
                // Ensures JVM closes after frame(s) closed and
                // all non-daemon threads are finished
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                // See https://stackoverflow.com/a/7143398/418556 for demo.
                f.setLocationByPlatform(true);

                // ensures the frame is the minimum size it needs to be
                // in order display the components within it
                f.pack();
                // should be done last, to avoid flickering, moving,
                // resizing artifacts.
                f.setVisible(true);
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }
}