具有可扩展单元格的动态布局

Posted

技术标签:

【中文标题】具有可扩展单元格的动态布局【英文标题】:Dynamic layout with expandable cells 【发布时间】:2011-10-27 07:08:50 【问题描述】:

我真的被 java swing 的布局所困扰。

我必须构建一个动态表单,同时遍历标签向量和输入组件。

应该是这样的:

在这张图片中,您可以看到应该是什么结果。绿线之间是一对标签/输入组件。我也有一个约束,告诉我,如果我必须将它们布置成一排 50/50 或两排或一排 25/75 或 75/25。此外,整个表格必须可调整大小,50/50、25/75 和 75/25 的比例仍然正确。

我尝试了 GridBagLayout 效果很好,但正如您在图片的第一行中看到的那样,标签可能很长。所以我使用了 JTextarea 但 GridBagLayout 将其切断。我也使用 JGoodies FormLayout,但在长标签的 Textarea 上也遇到了麻烦。

25/75 和 75/25 的比例并不那么重要,但实际上这也是我必须做的一部分。

我认为让 Textarea 扩展其父组件真的很困难。

我也读过/写过这些文章:

How can I create a JTextArea with a specified width and the smallest possible height required to display all the text? How to implement dynamic GUI in swing Expand JList row height depending on content

但我还是卡住了。

你们中有人知道如何实现这一点吗?

谢谢

【问题讨论】:

+1 但将此图片粘贴到您的帖子中,如果您想得到真正的答案,那么您必须确定哪些区域是/不可调整大小的,那么对我来说,您的问题是有道理的 也是固定文本的JTextArea,还是文本可以随着用户输入而改变? 不确定这是否对您有用,但也许您可以将 JSplitPane 用于必须拆分为 25% / 75% 部分的行。 【参考方案1】:

自从我做了很多 Swing 以来已经有一段时间了,但有两个想法:

    如果一个控件被切断,那听起来你没有设置最小尺寸。

    我认为任何现成的布局管理器都没有像 25/75 分割那样保持大小比例的功能。但不要绝望!您可以编写自己的布局管理器。当我做很多 Swinging 的时候,我写了几个布局管理器。 (我会说,“针对特殊需求”,但实际上它们是针对非常普遍的需求,Java 没有包含罐装布局管理器来处理这些事情总是让我感到惊讶。比如:你经常想要一行或一列按钮其中所有按钮的大小相同,因此您必须找到哪个按钮的文本最长,然后调整所有其他按钮的大小以匹配它。或者:设置行和列,其中每个列的大小为该列中最宽的东西,但一列的宽度对其他列没有影响;然后对行也是如此。)无论如何,您所描述的内容相当不寻常,因此您可能只想编写自己的布局管理器。

编写布局管理器的诀窍是实现几个关键功能: (a) 计算 X 和 Y 坐标,在给定整体尺寸的情况下放置每个控件; (b) 计算所需的最小尺寸; (c) 计算首选尺寸; (d) 计算您将使用的最大尺寸。嗯,可能还有一两个我忘记了,但这没什么大不了的。当我第一次想到“编写我自己的布局管理器”时,听起来就像是“编写自己的数据库引擎”或“编写自己的编译器”,但实际上并没有那么可怕。它通常是几百行代码。

当然,如果这里的其他人可以告诉您如何使用其中一个预设布局管理器来做您想做的事情,那肯定会容易得多。

【讨论】:

另见Creating a Custom Layout Manager。 @trashgod:谢谢。如果 OP 追求这一点,我相信他需要的帮助比我的两句话还要多。您可能还想查看现有布局管理器的源代码——这就是我想出的方法。 (如果您没有 Java 源代码,请从 Oracle 下载。它通常很有帮助。“使用源代码,Duke。”) 我终于编写了自己的布局管理器,它几乎可以完美运行。我现在只有滚动的问题,但我会在几分钟内为此打开一个新线程 ;-) 顺便说一句:你太棒了!【参考方案2】:

这是我到目前为止所得到的。此示例仅使用JTextArea,但它也适用于其他组件(如我的图片所示)。目前唯一不能做的就是像上图第 3 行那样布局组件(一个在另一个之上),但这应该没问题。

也许这有助于某人了解如何编写自定义布局管理器。

import java.awt.*;
import javax.swing.*;

public class SwingApp extends JFrame 

    private static final long serialVersionUID = 1L;

    public static void main(final String[] args) 
        SwingUtilities.invokeLater(new Runnable() 

            @Override
            public void run() 
                final SwingApp app = new SwingApp();
            
        );
    

    private SwingApp() 
        setTitle(":)");
        setSize(800, 450);
        setResizable(true);
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        /*JTextArea textArea = new JTextArea("111111alks jdfksajfjsaölk jfölksajfölkjsa 
        lkfj salkjflkdsjf ölsajfölksaj fölkjs flkjdsa flkjsaölkfjsölk");*/
        //textArea.setLineWrap(true);
        //this.getContentPane().add(textArea);
        this.getContentPane().setLayout(new MyListLayoutManager());
        add(20, "left ajdskfj lksajdf dsafj lkdsjflksjalkfjsalk jlksjaf salkjfjdsflkjdslkfjlks "
                + "flk salkfjdslkfjlkds flkjds lkfjdslkfjlkds flkjds fljlfd left", "right dksjf "
                + "asjfjsa jfsjföldsajföljsa ölkfjdsaölk jf0932875 0983 09584093859438 095843 "
                + "5lkjf lksa jf lkdsajf 923879 825094lkjtwj lkdsf27509425 right");
        add(50, "left ajdskfj lksajdf dsafj lkdsjflksjalkfjsalk jlksjaf salkjfjdsflkjdslkfjlks "
                + "flk salkfjdslkfjlkds flkjds lkfjdslkfjlkds flkjds fljlfd left", "right dksjf "
                + "asjfjsa jfsjföldsajföljsa ölkfjdsaölk jf0932875 0983 09584093859438 095843 "
                + "5lkjf lksa jf lkdsajf 923879 825094lkjtwj lkdsf27509425 right");
        add(70, "left ajdskfj lksajdf dsafj lkdsjflksjalkfjsalk jlksjaf salkjfjdsflkjdslkfjlks "
                + "flk salkfjdslkfjlkds flkjds lkfjdslkfjlkds flkjds fljlfd left", "right dksjf "
                + "asjfjsa jfsjföldsajföljsa ölkfjdsaölk jf0932875 0983 09584093859438 095843 "
                + "5lkjf lksa jf lkdsajf 923879 825094lkjtwj lkdsf27509425 right");
        add(70, "left ajdskfj lksajdf dsafj lkdsjflksjalkfjsalk jlksjaf salkjfjdsflkjdslkfjlks "
                + "flk salkfjdslkfjlkds flkjds lkfjdslkfjlkds flkjds fljlfd left", "right dksjf "
                + "asjfjsa jfsjföldsajföljsa ölkfjdsaölk jf0932875 0983 09584093859438 095843 "
                + "5lkjf lksa jf lkdsajf 923879 825094lkjtwj lkdsf27509425 right");
        add(70, "left ajdskfj lksajdf dsafj lkdsjflksjalkfjsalk jlksjaf salkjfjdsflkjdslkfjlks "
                + "flk salkfjdslkfjlkds flkjds lkfjdslkfjlkds flkjds fljlfd left", "right dksjf "
                + "asjfjsa jfsjföldsajföljsa ölkfjdsaölk jf0932875 0983 09584093859438 095843 "
                + "5lkjf lksa jf lkdsajf 923879 825094lkjtwj lkdsf27509425 right");
    

    private void add(final int leftPercent, final String leftText, final String rightText) 
        final JPanel panel = new JPanel(new MyRowLayoutManager(leftPercent));
        final JTextArea left = new JTextArea(leftText);
        left.setLineWrap(true);
        final JTextArea right = new JTextArea(rightText);
        right.setLineWrap(true);
        panel.add(left);
        panel.add(right);
        this.getContentPane().add(panel);
    

    private class MyListLayoutManager implements LayoutManager 

        @Override
        public void addLayoutComponent(final String name, final Component comp) 
        

        @Override
        public void removeLayoutComponent(final Component comp) 
        

        @Override
        public Dimension preferredLayoutSize(final Container parent) 
            return new Dimension(600, 300);
        

        @Override
        public Dimension minimumLayoutSize(final Container parent) 
            return new Dimension(0, 0);
        

        @Override
        public void layoutContainer(final Container parent) 
            final Dimension size = parent.getSize();
            final int left = 0;
            int top = 0;
            for (int i = 0; i < parent.getComponentCount(); ++i) 
                final Component component = parent.getComponent(i);
                component.setSize(size.width, 1);
                final Dimension preferredSize = component.getPreferredSize();
                component.setBounds(left, top, preferredSize.width, preferredSize.height);
                top += preferredSize.height;
            
        
    

    private class MyRowLayoutManager implements LayoutManager 

        private final int _leftPercent;

        private MyRowLayoutManager(final int leftPercent) 
            _leftPercent = leftPercent;
        

        @Override
        public void addLayoutComponent(final String name, final Component comp) 
        

        @Override
        public void removeLayoutComponent(final Component comp) 
        

        @Override
        public Dimension preferredLayoutSize(final Container parent) 
            return new Dimension(600, 300);
        

        @Override
        public Dimension minimumLayoutSize(final Container parent) 
            return new Dimension(0, 0);
        

        @Override
        public void layoutContainer(final Container parent) 
            if (parent.getComponentCount() != 2) 
                throw new RuntimeException("Supports for exact 2 components");
            
            final Dimension size = getSize();
            final int widthForLeft = size.width * _leftPercent / 100;
            final int widthForRight = size.width - widthForLeft;
            final Component left = parent.getComponent(0); //left Component
            left.setSize(widthForLeft, 1);
            final Dimension leftDimension = left.getPreferredSize();
            left.setBounds(0, 0, widthForLeft, leftDimension.height);
            final Component right = parent.getComponent(1); //right Component
            right.setSize(widthForRight, 1);
            final Dimension rightDimension = right.getPreferredSize();
            right.setBounds(widthForLeft, 0, widthForRight, rightDimension.height);
            parent.setSize(size.width, Math.max(leftDimension.height, rightDimension.height));
            parent.setPreferredSize(new Dimension(size.width, Math.max(leftDimension.height, rightDimension.height)));
        
    

通过将 main 方法包装到 invokeLater() 中可以看到:

SwingUtilities.invokeLater(new Runnable() 

        @Override
        public void run() 
            final SwingApp app = new SwingApp();
        
    );

【讨论】:

以上是关于具有可扩展单元格的动态布局的主要内容,如果未能解决你的问题,请参考以下文章

具有动态大小的单元格的复杂自动布局

自动布局动态大小 uitableview 单元格的最佳方法是啥

UITableViewCell 具有嵌入式垂直堆栈视图设置,具有自动布局和动态高度

使用自动布局自定义集合视图单元格的动态高度

使用自动布局约束动态调整表格视图单元格的大小

当我使用自动布局来选择每个单元格的高度(因此它们是动态的)时,它主要工作,但有些单元格有点短。为啥是这样?