开发者

Swing: proper layout manager interaction with upper-level layout manager?

I'm experimenting with a custom LayoutManager and I don't understand the subtleties of using it within a component that itself has an upper-level layout manager.

Swing: proper layout manager interaction with upper-level layout manager?

Below is a test program that makes two frames with pair of JPanels. Each of the two JPanels has a thin black border and uses my WeirdGridLayout to force its child components into squares in a grid, with the JPanel height being computed from the width. Both JPanels are in another JPanel with a thin red border that uses BorderLayout.

In one frame, the JPanels with WeirdGridLayout are arranged EAST and WEST, the other arranged NORTH and SOUTH.

The problem is that in the north/south case, if I change the width/height of the frame, the two JPanels with WeirdGridLayout have the right size, but not the right position (they either have a gap or overlap vertically).

Swing: proper layout manager interaction with upper-level layout manager?

In the east/west case, it just ends up wrong.

Swing: proper layout manager interaction with upper-level layout manager?

What I have to do to get my layout manager to play well with outer layout managers?

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.LayoutManager2;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 */
public class WeirdGridLayout implements LayoutManager2
{


    static final private int GRIDGAP = 10; 
    static final private int COMPONENT_SIZE = 30;
    static final private int GRIDSPACING = COMPONENT_SIZE + GRIDGAP;

    final private List<Component> components
        = new ArrayList<Component>();

    @Override public void addLayoutComponent(Component comp, Object constraints) {
        this.components.add(comp);
    }
    @Override public void addLayoutComponent(String name, Component comp) {
        this.components.add(comp);
    }
    @Override public void removeLayoutComponent(Component comp) {
        this.components.remove(comp);
    }   

    @Override public float getLayoutAlignmentX(Container target) {
        return Component.LEFT_ALIGNMENT;
    }
    @Override public float getLayoutAlignmentY(Container target) {
        return Component.TOP_ALIGNMENT;
    }

    @Override public void invalidateLayout(Container target) {}
    @Override public Dimension maximumLayoutSize(Container target) {
        return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
    }
    @Override public Dimension minimumLayoutSize(Container parent) {
        return new Dimension(0,0);
    }

    @Override public void layoutContainer(Container parent) {
        int x = GRIDGAP;
        int y = GRIDGAP;

        Dimension d = preferredLayoutSize(parent);
        parent.setSize(d);
        for (Component component : this.components)
        {
            component.setBounds(x, y, COMPONENT_SIZE, COMPONENT_SIZE);

            x += GRIDSPACING;
            if (x >= d.getWidth())
            {
                x = GRIDGAP;
                y += GRIDSPACING;
            }
        }
    }

    @Override public Dimension preferredLayoutSize(Container parent) {
        // how many blocks wide can we fit?
        int n = this.components.size();
        int nblockwidth = (parent.getWidth() - GRIDGAP) / GRIDSPACING;
        int nblockheight = (nblockwidth == 0) ? 0 
                : ((n-1)/nblockwidth) + 1;      
        return new Dimensio开发者_如何学Cn(
                nblockwidth*GRIDSPACING+GRIDGAP,
                nblockheight*GRIDSPACING+GRIDGAP);
    }

    /* ---- test methods ---- */

    static public class ColorPanel extends JPanel {
        final private Color color;
        final private String label;
        public ColorPanel(String label, Color color) { 
            this.label = label;
            this.color = color; 
        }
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(this.color);
            g.fillRect(0,0,getWidth(),getHeight());
            g.setColor(Color.WHITE);
            FontMetrics fm = g.getFontMetrics();
            int w = fm.stringWidth(this.label);
            g.drawString(this.label, (getWidth()-w)/2,
                    (getHeight()+fm.getAscent())/2);
        } 
    }

    public static void main(String[] args) {
        showFrame(true);
        showFrame(false);
    }
    private static void showFrame(boolean eastWest) {
        JFrame frame = new JFrame("WeirdGridLayout test: eastWest="+eastWest);
        JPanel framePanel = new JPanel(new BorderLayout());
        framePanel.setPreferredSize(new Dimension(400,200));

        JPanel panel[] = new JPanel[2];
        for (int i = 0; i < 2; ++i)
        {
            panel[i] = new JPanel(new WeirdGridLayout());
            panel[i].setBorder(BorderFactory.createLineBorder(Color.BLACK));
            final Random r = new Random();
            for (int j = 0; j < 24; ++j)
            {
                Color c = new Color(
                            r.nextFloat(),
                            r.nextFloat(),
                            r.nextFloat());
                JPanel subpanel = new ColorPanel(Integer.toString(j), c);
                panel[i].add(subpanel);
            }
        }

        framePanel.add(new JButton("test"), BorderLayout.NORTH);
        JPanel bottomPanel = new JPanel(new BorderLayout());
        framePanel.add(bottomPanel, BorderLayout.SOUTH);

        if (eastWest)
        {
            bottomPanel.add(panel[0], BorderLayout.WEST);
            bottomPanel.add(panel[1], BorderLayout.EAST);
        }
        else
        {
            bottomPanel.add(panel[0], BorderLayout.NORTH);
            bottomPanel.add(panel[1], BorderLayout.SOUTH);          
        }
        bottomPanel.setBorder(BorderFactory.createLineBorder(Color.RED));


        frame.setContentPane(framePanel);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}


Answering the subject: none :-) LayoutManager is not designed for interaction with other LayoutManagers, they act in isolation on the target they are responsible for.

  • LayoutManager is responsible for sizing and positioning the children of container, not the container itself. So WeirdLayoutManager is misbehaving in setting the size of the parent to its pref.
  • preferredLayoutSize must return something reasonable always: regard it as kind-of detached, something like "dear container, given you had all the space of the world, what size exactly would you like to have" Or the other way round: don't rely on the parent size to answer the question. It would be like a dog trying to bite into its own tail. For a grid-like structure, that probably requires some kind of prefColumns/-Rows property
  • layoutContainer must size and position the direct children inside the current bounds of the container, don't touch the container itself. It can do so in any way it likes, in as many rows/columns as needed


The key is to understand what the top level layout manager is doing and making sure the lower level has a preferred size or other attribute set to have it render correctly.

From the API (http://download.oracle.com/javase/7/docs/api/java/awt/BorderLayout.html):

The NORTH and SOUTH components may be stretched horizontally; the EAST and WEST components may be stretched vertically; the CENTER component may stretch both horizontally and vertically to fill any space left over.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜