Swing:布局管理器与上层布局管理器的正确交互?
我正在尝试自定义 LayoutManager,但我不明白在本身具有上层布局管理器的组件中使用它的微妙之处。
下面是一个测试程序,它使用一对 JPanel 制作两个框架。两个 JPanel 中的每一个都有一个细黑色边框,并使用我的 WeirdGridLayout 强制其子组件进入网格中的正方形,JPanel 高度是根据宽度计算的。两个 JPanel 都位于另一个 JPanel 中,该 JPanel 具有使用 BorderLayout 的红色细边框。
在一个框架中,具有 WeirdGridLayout 的 JPanel 排列为东和西,其他排列 排列为北和南。
问题是,在北/南的情况下,如果我改变框架的宽度/高度, 两个具有 WeirdGridLayout 的 JPanel 具有正确的尺寸,但位置不正确(它们要么有间隙,要么垂直重叠)。
在东/西情况下,结果就是错误的。
我需要做什么才能让我的布局管理器与外部布局管理器良好配合?
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);
}
}
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.
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).
In the east/west case, it just ends up wrong.
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 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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
回答主题:无:-) LayoutManager 不是为与其他 LayoutManager 交互而设计的,它们在它们负责的目标上独立运行。
Answering the subject: none :-) LayoutManager is not designed for interaction with other LayoutManagers, they act in isolation on the target they are responsible for.
关键是要了解顶级布局管理器正在做什么,并确保较低级别具有首选大小或其他属性集以使其正确呈现。
从 API (http://download.oracle.com/javase/7/docs/api/java/awt/BorderLayout.html) 中:
NORTH 和 SOUTH 组件可以水平拉伸; EAST 和 WEST 组件可以垂直拉伸;CENTER 组件可以水平和垂直拉伸以填充剩余的空间。
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.