【问题标题】:JPanels don't completely stretch to occupy the available spaceJPanel 不会完全伸展以占用可用空间
【发布时间】:2012-03-02 01:10:52
【问题描述】:

我有一个面板,我在其中并排放置了几个具有不同尺寸和颜色的迷你面板,它们应该占据整个父面板(​​水平方向)。

为此,我使用 BorderLayout(用于父面板)和 BoxLayout 用于放置所有迷你面板的子面板(参见下面的代码)。它确实可以在调整大小和所有内容时正常工作并正常运行。但是,随着迷你面板的数量越来越多,出现了一个奇怪的行为:父面板的末尾出现空白。

我想我发现这是布局管理器中的一个拉伸错误,因为为了拉伸面板,布局管理器会尝试向每个迷你面板添加一个像素。但是,当 mini-panel 的数量很大时,每个都添加一个像素会导致添加许多像素并超出父级的大小。因此,布局管理器最终不会向任何迷你面板添加任何像素,从而导致空白空间。

这是我的 SSCCE: (尝试运行并拉伸窗口以了解问题)

package com.myPackage;

import java.awt.*;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class ColoredPanels extends JPanel
{
    /* Content information. */
    private Vector<Integer> partitions;
    private Vector<Color> colors;

    /* Panel where the content panels will go. */
    private JPanel contentHolder;

    private final int defaultHeight = 20;

    public ColoredPanels(Vector<Integer> partitions, Vector<Color> colors)
    {
        assert partitions != null;
        assert !partitions.isEmpty();
        assert colors != null;
        assert !colors.isEmpty();
        assert colors.size() == partitions.size();

        this.partitions = partitions;
        this.colors = colors;

        /* Set layout manager. */
        setLayout(new BorderLayout());

        /* Create the content holder. */
        contentHolder = new JPanel();
        contentHolder.setLayout(new BoxLayout(contentHolder, BoxLayout.X_AXIS));
        this.add(contentHolder, BorderLayout.NORTH);

        /* Fill content holder with colored panels. */
        createPanels();
    }

    private void createPanels()
    {
        assert partitions != null;
        assert !partitions.isEmpty();
        assert colors != null;
        assert !colors.isEmpty();
        assert colors.size() == partitions.size();

        for (int i = 0; i < partitions.size(); i++)
        {
            JPanel newPanel = new JPanel();
            newPanel.setBackground(colors.get(i));
            newPanel.setPreferredSize(new Dimension(partitions.get(i), defaultHeight));
            newPanel.setMinimumSize(new Dimension(1, defaultHeight));
            contentHolder.add(newPanel);
        }
    }

    public static void main(String[] in)
    {
        Vector<Integer> sizes = new Vector<Integer>();
        Vector<Color> cols = new Vector<Color>();

        /* Make 100 random sizes, and use two colors. */
        for (int i = 0; i < 100; i++)
        {
            int size = (int)Math.round(1 + Math.random() * 10);
            sizes.add(size);
            cols.add((i%2 == 0)? Color.red : Color.green);
        }

        ColoredPanels panels = new ColoredPanels(sizes, cols);
        panels.setBorder(BorderFactory.createLineBorder(Color.yellow, 1));

        JFrame newFrame = new JFrame();
        newFrame.getContentPane().add(panels);
        newFrame.pack();
        newFrame.setVisible(true);
    }
}

如何避免这种行为?我希望我的面板占据整个容器。

编辑: 迷你面板旨在具有(一旦解决)鼠标侦听器。因此,不幸的是,绘画解决方案是可以避免的。

【问题讨论】:

  • 有趣...您想要发生什么?最简单(很可能不是最好的:-)出路是将所有舍入空间留给最后一个......
  • 我希望迷你面板完全填满可用空间。将剩余部分放在最后不是一个选项,因为必须遵守分区大小比率。

标签: java swing awt jpanel border-layout


【解决方案1】:

布局问题由... LayoutManagers 解决 :-) 如果一个人不满足您的要求,请实现您想要的行为。

因此,如果核心 BoxLayout 由于舍入误差而简单地忽略像素,则子类化并使其根据需要分配这些像素。一个非常原始的快速示例:

public static class XBoxLayout extends BoxLayout {

    enum Strategy {
        NONE,
        STRETCH_LAST,
        DISTRUBUTE
    }

    private Strategy strategy;

    public XBoxLayout(Container target, int axis, Strategy strategy) {
        super(target, axis);
        this.strategy = strategy;
    }


    @Override
    public void layoutContainer(Container target) {
        super.layoutContainer(target);
        if (Strategy.NONE == strategy) return;
        Insets targetInsets = target.getInsets();
        int targetSize = target.getWidth() - targetInsets.left - targetInsets.right;
        int childSum = 0;
        for (Component child : target.getComponents()) {
            childSum += child.getWidth();
        }
        if (targetSize > childSum) {
            int excess = targetSize - childSum;
            distribute(target, excess);
        }
    }


    private void distribute(Container target, int excess) {
        System.out.println("childCount/rounding excess " + target.getComponentCount() + "/" + excess);
        if (Strategy.STRETCH_LAST == strategy) {
            Component lastChild = target.getComponent(target
                    .getComponentCount() - 1);
            lastChild.setSize(lastChild.getWidth() + excess,
                    lastChild.getHeight());
        } else {
            int firstToDistribute = target.getComponentCount() - excess;
            int summedOffset = 0;
            for(int index = firstToDistribute; index < target.getComponentCount(); index++) {
                Component child = target.getComponent(index);
                Rectangle bounds = child.getBounds();
                bounds.x += summedOffset++;
                bounds.width += 1;
                child.setBounds(bounds);
            }
        }
    }

}

【讨论】:

  • 感谢您的帮助。这看起来确实是一个可行的解决方案,我正在寻找涉及布局管理器的东西。即使使用 DISTRIBUTION 策略,代码看起来也不尊重组件大小比例(彼此相关),但我明白了这一点,并将尝试在这些行中实现一些东西。
  • 我将支持这一点 - 当您需要此类自定义行为时,创建自己的布局管理器并不是那么痛苦,您可以针对您的特定用例对其进行优化。
  • @Anoyz 分配单个像素是 .. 一个策略问题 :-) 实现你需要的任何东西,最终它不会与实际/首选/任何大小完全成比例跨度>
【解决方案2】:

我能想到的两个选项:

  1. 拥有一个自定义组件,该组件具有绘制所有区域的paintComponent(...) 方法,当调整窗口大小时,它会重新绘制区域,同时根据新的宽度信息对其进行缩放。

    李>
  2. 在图像上绘制一次彩色区域,然后绘制该区域,然后在调整窗口大小时重新缩放图像以适合。

【讨论】:

  • 感谢您的帮助。我打算将鼠标侦听器添加到迷你面板。如果我使用绘画策略,我该怎么做?
  • 有必要将监听器添加到底层组件(例如面板),然后从面板的缩放尺寸向后缩放坐标。
  • 没有更简单的布局解决方案吗?
【解决方案3】:

我曾经在为棋盘使用 GridLayout 时遇到过这个问题。我的解决方案是扩展 GridLayout 并以浮点精度计算布局。

【讨论】:

  • 您能否对该解决方案提供更多见解?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-07-08
  • 1970-01-01
  • 2018-07-28
  • 2018-02-07
  • 1970-01-01
  • 2021-01-21
相关资源
最近更新 更多