【问题标题】:Return actual size of JComponent, as displayed返回 JComponent 的实际大小,如图所示
【发布时间】:2010-08-14 07:08:58
【问题描述】:

晚上好,

我想知道在 JComponent 使用 LayoutManager 布局后如何获取它的大小。我已经读过这是可能的,如果您事先调用 dolayout()、validate() 或 setVisible()。但是,我无法让它在我的代码中工作。

我想知道这一点的原因是只添加尽可能多的组件以适合框架的设置大小,而事先不知道组件的大小。调用 validate() 不会设置此代码示例中组件的大小。关于如何获得合适尺寸的任何想法?

public class TestFrame extends JFrame {

    public static void main(String args[]) {
        TestFrame frame = new TestFrame();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public TestFrame() {
        super("Test Frame");
        super.setSize(300, 600);
        this.addComponents();
    }

    private void addComponents() {

        int heightLeft = this.getHeight();
        JPanel panel = new JPanel();
        panel.setSize(300, 600);
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
        String text = "This is a long String that represents "
            + "the length of strings in my application. "
            + "This will vary in the real application."
            + "<ul><li>It uses html</li><li>not just</li>"
            + "<li>plain text</li></ul>"
            + "I'd like as many as will fit in the fame to be added.\n";

        while (true) {
            JTextPane textPane = createTextPane(text);

            this.invalidate();

            if (heightLeft > 0) {
                panel.add(textPane);
            } else {
                break;
            }
            System.out.println("Before validation:" + textPane.getSize());
            // returns width and height = 0
            this.validate();
            System.out.println("After validation:" + textPane.getSize());
            // Still returns width and height = 0
            heightLeft -= textPane.getPreferredSize().getHeight();
        }

        super.add(panel);
    }

    public JTextPane createTextPane(String text) {
        JTextPane textPane = new JTextPane();

        textPane = new JTextPane();
        textPane.setEditorKit(new StyledEditorKit());
        textPane.setEditable(false);
        textPane.setOpaque(false);

        textPane.setContentType("text/html");
        textPane.setText(text);

        return textPane;
    }
}

感谢您的宝贵时间!

【问题讨论】:

  • 重新格式化的代码;如果不正确,请恢复。
  • 感谢您重新格式化。它看起来更好。 JScrollPane 绝对是我的后备选项。我不认为它看起来太糟糕,但我想出了让应用程序看起来像某种方式的想法,团队中的其他人都非常喜欢这个想法,现在我正试图弄清楚如何这样做:p

标签: java swing size


【解决方案1】:

您要完成的工作似乎异常困难,因为您必须依靠 Swing 的方法来计算各种组件的大小,然后设法获取正确的值以对它们执行计算。虽然基于可用的 API 方法看起来相当微不足道,但看一下源代码就会发现一个不同的故事。由于某些值仅在实际显示组件时才正确计算,这也使这更加复杂,这意味着您在此之前尝试获得的值并不代表您在屏幕上看到的内容(正如您所发现的那样)。

除此之外,似乎应该可以按照您的目标进行操作。请记住,在向BoxLayout 添加元素时,预期的行为是您分配了布局管理器的容器的整个空间都用于定位子组件。这意味着,当您将 JTextPane 对象添加到您的 JPanel 时,它们将被拉伸,以使它们的总高度占据您指定的所有 600 个像素。因此,您需要稍微不同地确定何时超出可用高度,因为每个组件的渲染高度可能会在进程中发生变化。

在玩了一会儿并反复诅咒 Swing 之后,我虽然想出了一个解决方案,方法是使用 getBounds()BoxLayout 使用它委托给它的 LayoutParameters 来确定组件高度),但是我发现的方法不是很可靠。我将继续使用它,所以希望我能提出一些可用的代码,或者其他人会提出我忽略的解决方案。

编辑:为了完整起见,这里有一个代码示例,它调用doLayout() 在显示表单之前计算必要的大小(仍然有BoxLayout 的缺点)。由于SizeRequirements 中有一些ArrayIndexOutOfBoundsException,它不会在我的JRE 1.5 上运行,但该错误似乎已在1.6 中修复。在 1.5 中,如果您的组件列表不是太大,您可以将它们全部添加,在循环之后调用 doLayout()(因为无论出于何种原因,这似乎在 1.5 中有效),然后只需遍历 getComponents() 返回的数组,在超过最大高度后删除所有内容。

最后一点,我个人喜欢使用MigLayout 来处理更复杂的布局,因为我发现它的定位和样式机制比当前的内置布局管理器更容易使用。在这里并不过分相关,但无论如何值得一提。

import java.awt.Dimension;
import java.awt.Rectangle;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.text.StyledEditorKit;

public class TestFrame extends JFrame {
    private static final int CONTENT_HEIGHT = 600;
    private static final int CONTENT_WIDTH = 300;

    public static void main(String[] args) {
        TestFrame frame = new TestFrame();

        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public TestFrame() {
        super("Test Frame");
        this.addComponents();
        this.pack();
    }

    private void addComponents() {
        JPanel panel = new JPanel();

        panel.setPreferredSize(new Dimension(CONTENT_WIDTH, CONTENT_HEIGHT));
        panel.setBounds(new Rectangle(CONTENT_WIDTH, CONTENT_HEIGHT));
        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

        String text = "This is a long String that represents the length of" +
            " stings in my application. This will vary in the real" + 
            " application.<ul><li>It uses html</li><li>not just</li>" +
            "<li>plain text</li></ul>I'd like only as many as will fit" +
            " in the fame to be added.\n";

        this.setContentPane(panel);

        int height = 0;

        while(height < CONTENT_HEIGHT) {
            JTextPane textPane = createTextPane(text);

            panel.add(textPane);
            panel.doLayout();

            // The height on the preferred size has been set to reflect
            // the rendered size after calling doLayout()
            height += textPane.getPreferredSize().getHeight();

            // If we've exceeded the height, backtrack and remove the
            // last text pane that we added
            if (height > CONTENT_HEIGHT) {
                panel.remove(textPane);
            }
        }
    }

    private JTextPane createTextPane(String text) {
        JTextPane textPane = new JTextPane();

        textPane.setEditorKit(new StyledEditorKit());
        textPane.setEditable(false);
        textPane.setOpaque(false);
        textPane.setContentType("text/html");
        textPane.setText(text);

        return textPane;
    }
}

【讨论】:

  • 感谢您对此进行如此详细的研究。我不知道 BoxLayout 是这样工作的。我将研究其他可能避免此问题的 LayoutManager。在为所有组件设置固定宽度后,也许 FlowLayout ? (让组件像页面上的段落一样流动是很重要的。)玩弄它并诅咒 Swing 是我一直在做的大部分事情。很高兴我并不孤单。
  • 没问题,这是一个有趣的问题。 FlowLayout 可能会像你想要的那样工作,只要你给容器一个固定宽度。这可能需要使用setPreferredSize() 来完成,尽管考虑到其他选项(setSize()setMaximumSize()),这似乎有点违反直觉。 Swing 有很多好的意图,但最终的实现有时会错过我的感觉。不过,一旦你习惯了它并没有那么糟糕。
  • 有效!完美!谢谢!这是一个足够好的解决方案来解决我的问题,尽管我会使用不同的 LayoutManagers,因为对于我的项目来说,最好的设计是让所有文本在页面顶部聚集在一起,就像工作表上的文本一样尽可能多的纸张。
【解决方案2】:

显示组件的框架必须可见才能获得大小(因为如果框架不可见,布局管理器会将大小设置为 (0, 0))。在添加组件之前,您必须调用 frame.setVisible(true)。我还建议您创建自己的面板类(它将扩展 JPanel)并创建组件并将它们从那里放置在面板上,而不是从主类中。

【讨论】:

  • 在我的代码中执行此操作使大小按预期设置。谢谢。但是,正如 Tim 指出的那样,BoxLayout 会拉伸组件,因此只添加了 2 个 textPanel,没有适合框架的那么多。
  • 是的,在阅读了蒂姆的评论后,我意识到了这一点。您绝对应该使用另一个布局管理器。一直使用GridBagLayout,一开始可能会觉得有点复杂,但是从来没有给我带来太大的麻烦,所以推荐大家使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多