Swing: правильное взаимодействие менеджера макета с менеджером макета верхнего уровня? - PullRequest
2 голосов
/ 13 июня 2011

Я экспериментирую с пользовательским LayoutManager и не понимаю тонкостей его использования в компоненте, который сам имеет менеджер компоновки верхнего уровня.

enter image description here

Ниже тестовая программа, которая делает два кадра с парой JPanels.Каждый из двух JPanel имеет тонкую черную рамку и использует my WeirdGridLayout, чтобы заставить его дочерние компоненты в квадраты в сетке, причем высота JPanel вычисляется по ширине.Оба JPanel находятся в другом JPanel с тонкой красной рамкой, которая использует BorderLayout.

В одном кадре JPanels с WeirdGridLayout располагаются EAST и WEST, а остальные - NORTH и SOUTH.

Проблемазаключается в том, что в случае север / юг, если я изменю ширину / высоту рамки, два JPanels с WeirdGridLayout имеют правильный размер, но не правильную позицию (они либо имеют зазор, либо перекрываются по вертикали).

enter image description here

В случае с востоком / западом все просто заканчивается неправильно.

enter image description here

Что мне нужно сделать, чтобы мой менеджер по расположениюхорошо играть с внешними менеджерами макетов?

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 Dimension(
                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);
    }
}

Ответы [ 2 ]

2 голосов
/ 13 июня 2011

Ключ в том, чтобы понять, что делает менеджер компоновки верхнего уровня, и убедиться, что нижний уровень имеет предпочтительный размер или другой атрибут, установленный для правильной визуализации.

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

Компоненты СЕВЕР и ЮГ могут быть растянуты по горизонтали ; Компоненты EAST и WEST могут быть растянуты по вертикали; Компонент CENTER может растягиваться как по горизонтали, так и по вертикали, чтобы заполнить любое оставшееся пространство.

1 голос
/ 14 июня 2011

Отвечая на вопрос: нет :-) LayoutManager не предназначен для взаимодействия с другими LayoutManager, они действуют изолированно от цели, за которую они отвечают.

  • LayoutManager отвечает за определение размера и расположение дочерних элементов контейнера, а не самого контейнера. Итак, WeirdLayoutManager неправильно работает, устанавливая размер родительского элемента в соответствии с его префом.
  • favouriteLayoutSize всегда должен возвращать что-то разумное: считайте это чем-то вроде отдельного, что-то вроде «дорогой контейнер, если у вас есть все пространство мира, какого размера вы бы хотели иметь» или наоборот: не полагайтесь на размер родителей, чтобы ответить на вопрос. Это было бы как собака, пытающаяся укусить себя в хвост. Для решетчатой ​​структуры это, вероятно, требует некоторого свойства prefColumns / -Rows
  • layoutContainer должен определять размер и расположение прямых потомков внутри текущих границ контейнера, не касаясь самого контейнера. Он может делать это любым удобным для него способом, в необходимом количестве строк / столбцов
...