当前位置: 首页 > 知识库问答 >
问题:

如何在Java中使JTextArea占据GridBagLayout中最大宽度的80%

耿建弼
2023-03-14

我正在尝试创建一个聊天窗口,但我似乎不知道如何使JTextArea占据GridBagLayout中可用宽度空间的最大80%。换言之,如果文本似乎占据了窗口的整个宽度,那么它应该只能占据宽度的80%,只需换行即可。此外,如果文本小于80%,比如说40%,那么JTextArea应该只包装40%,而不是整个80%
我使用的是GridBagLayout,我已将GridBagConstraints weightx设置为1.0,并将填充设置为水平,左侧或右侧插入40,具体取决于用户。但我似乎不能让JTextArea来包装文本,如果需要,允许它使用窗口宽度的80%,否则就要包装文本。



下面是我编写的当前代码:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;


public class ChatWindow {

    private JFrame mMainFrame;

    public ChatWindow(List<ChatEntry> chatContentList) {
        // Create JFrame, set window size and center on screen.
        mMainFrame = new JFrame();
        mMainFrame.setTitle("Chat Window");
        mMainFrame.setSize(360, 600);
        mMainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mMainFrame.setLocationRelativeTo(null);

        // Create parent container JPanel for all other JComponents.
        JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.weightx = 1.0;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        mainPanel.add(buildChatUI(chatContentList), gbc);

        // Add empty JPanel as an object to fill the empty space available.
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weighty = 1.0;
        mainPanel.add(new JPanel(), gbc);

        mMainFrame.getContentPane().add(new JScrollPane(new VerticalScrollPane(mainPanel)));
        mMainFrame.setVisible(true);
    }

    public JPanel buildChatUI(List<ChatEntry> chatContentList) {
        JPanel chatPanel = new JPanel();
        chatPanel.setLayout(new GridBagLayout());
        chatPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        GridBagConstraints gbc = new GridBagConstraints();

        for (ChatEntry chatEntry : chatContentList) {
            JLabel nameLabel = new JLabel(chatEntry.name);

            JTextArea contentTextArea = new JTextArea();
            contentTextArea.setText(chatEntry.content);
            contentTextArea.setOpaque(true);
            contentTextArea.setLineWrap(true);
            contentTextArea.setWrapStyleWord(true);
            contentTextArea.setEditable(false);

            // Arrange each chat entry based on the user.
            if (chatEntry.type == 1) {
                contentTextArea.setBackground(Color.YELLOW);
                gbc.anchor = GridBagConstraints.WEST;
            }
            else {
                contentTextArea.setBackground(Color.CYAN);
                gbc.anchor = GridBagConstraints.EAST;
            }

            gbc.insets.set(0, 0, 0, 0);
            gbc.weightx = 1.0;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.fill = GridBagConstraints.NONE;
            chatPanel.add(nameLabel, gbc);

            if (gbc.anchor == GridBagConstraints.WEST) {
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.insets.set(0, 0, 0, 40);
                chatPanel.add(contentTextArea, gbc);
            }
            else {
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.insets.set(0, 40, 0, 0);
                chatPanel.add(contentTextArea, gbc);
            }
        }

        return chatPanel;
    }


    /**
     * This class is used to make the JTextArea lines wrap every time the window
     * is resized. Without this, the JTextArea lines will not shrink back if the
     * parent window shrinks. This is achieved by returning true on getScrollableTracksViewportWidth();
     */
    private class VerticalScrollPane extends JPanel implements Scrollable {

        private static final long serialVersionUID = 7477168367035025136L;

        public VerticalScrollPane() {
            this(new GridLayout(0, 1));
        }

        public VerticalScrollPane(LayoutManager lm) {
            super(lm);
        }

        public VerticalScrollPane(Component comp) {
            this();
            add(comp);
        }

        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return getPreferredSize();
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect,
                int orientation, int direction) {
            return 10;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect,
                int orientation, int direction) {
            return 100;
        }

        @Override
        public boolean getScrollableTracksViewportWidth() {
            return true;
        }

        @Override
        public boolean getScrollableTracksViewportHeight() {
            return false;
        }

    }

    /**
     * Class structure for storing a single chat entry in a full conversation.
     */
    public static class ChatEntry {
        public String name;
        public String content;

        // For type 0=sent, 1=received.
        public int type;

        public ChatEntry(String name, String content, int type) {
            this.name = name;
            this.content = content;
            this.type = type;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // Simulate a conversation between two users and pass it on to the
                // chat window to test the UI.
                List<ChatEntry> chatContentList = new LinkedList<>();
                chatContentList.add(new ChatEntry("David", "Hey Lori, how are you?", 0));
                chatContentList.add(new ChatEntry("Lori", "Hi David, I'm good. What have you been up to?", 1));
                chatContentList.add(new ChatEntry("David", "I've been super busy with work.", 0));
                chatContentList.add(new ChatEntry("David", "Haven't had much free time to even go out to eat.", 0));
                chatContentList.add(new ChatEntry("Lori", "I know what you mean, I've had to work on projects after projects.", 1));
                chatContentList.add(new ChatEntry("David", "Let's make some time and go to lunch tomorrow!", 0));
                chatContentList.add(new ChatEntry("Lori", "That sounds great, let's do 12pm. I know a great food truck by my building.", 1));
                chatContentList.add(new ChatEntry("David", "Perfect, I'll meet you at the entrance of your building.", 0));
                chatContentList.add(new ChatEntry("Lori", "Awesome, see you tomorrow :)", 1));
                new ChatWindow(chatContentList);
            }
        });
    }

}

共有2个答案

翟卓君
2023-03-14

代码如下:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;


public class ChatWindow {

    private JFrame mMainFrame;

    public ChatWindow(List<ChatEntry> chatContentList) {
        // Create JFrame, set window size and center on screen.
        mMainFrame = new JFrame();
        mMainFrame.setTitle("Chat Window");
        mMainFrame.setSize(360, 600);
        mMainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mMainFrame.setLocationRelativeTo(null);

        // Create parent container JPanel for all other JComponents.
        JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.weightx = 1.0;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        mainPanel.add(buildChatUI(chatContentList), gbc);

        // Add empty JPanel as an object to fill the empty space available.
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weighty = 1.0;
        mainPanel.add(new JPanel(), gbc);

        mMainFrame.getContentPane().add(new JScrollPane(new VerticalScrollPane(mainPanel)));
        mMainFrame.setVisible(true);
    }

    public JPanel buildChatUI(List<ChatEntry> chatContentList) {
        JPanel chatPanel = new JPanel();
        chatPanel.setLayout(new GridBagLayout());
        chatPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        GridBagConstraints gbc = new GridBagConstraints();

        for (ChatEntry chatEntry : chatContentList) {
            JLabel nameLabel = new JLabel(chatEntry.name);

            BubblePane bubble = new BubblePane(chatPanel, chatEntry.content);

            // Arrange each chat entry based on the user.
            if (chatEntry.type == 1) {
                bubble.setBackground(Color.YELLOW);
                gbc.anchor = GridBagConstraints.WEST;
            }
            else {
                bubble.setBackground(Color.CYAN);
                gbc.anchor = GridBagConstraints.EAST;
            }

            gbc.insets.set(0, 0, 0, 0);
            gbc.weightx = 1.0;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.fill = GridBagConstraints.NONE;
            chatPanel.add(nameLabel, gbc);

            if (gbc.anchor == GridBagConstraints.WEST) {
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.insets.set(0, 0, 10, 40);
                chatPanel.add(bubble, gbc);
            }
            else {
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.insets.set(0, 40, 10, 0);
                chatPanel.add(bubble, gbc);
            }
        }

        return chatPanel;
    }

    private class BubblePane extends JTextArea {

        private static final long serialVersionUID = -6113801969569504295L;

        private int radius = 10;
        private int strokeThickness = 3;
        private int padding = strokeThickness / 2;
        private JPanel mParent;

        public BubblePane(JPanel parent, String text) {
            mParent = parent;

            setOpaque(false);
            setLineWrap(true);
            setWrapStyleWord(true);
            setEditable(false);
            setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
            setText(text);
        }

        @Override
        public void paintComponent(Graphics g) {
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(getBackground());
            int x = padding + strokeThickness;
            int width = getWidth() - (strokeThickness * 2);
            int bottomLineY = getHeight() - strokeThickness;
            g2d.fillRect(x, padding, width, bottomLineY);
            g2d.setRenderingHints(new RenderingHints(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON));
            g2d.setStroke(new BasicStroke(strokeThickness));
            RoundRectangle2D.Double rect = new RoundRectangle2D.Double(x, padding,
                    width, bottomLineY, radius, radius);
            Area area = new Area(rect);
            g2d.draw(area);


            int lc = countLines(this);
            GridBagLayout gbl = (GridBagLayout) mParent.getLayout();
            GridBagConstraints constraints = gbl.getConstraints(this);
            if (lc == 1) {
                if (constraints.fill == GridBagConstraints.HORIZONTAL) {
                    constraints.fill = GridBagConstraints.NONE;
                    gbl.setConstraints(this, constraints);
                    this.setSize(
                            getFontMetrics(getFont()).stringWidth(getText()) + 
                            this.getBorder().getBorderInsets(this).left +
                            this.getBorder().getBorderInsets(this).right, 
                            getHeight() +
                            this.getBorder().getBorderInsets(this).top + 
                            this.getBorder().getBorderInsets(this).bottom);
                }
            } else {
                if (constraints.fill == GridBagConstraints.NONE) {
                    constraints.fill = GridBagConstraints.HORIZONTAL;
                    gbl.setConstraints(this, constraints);

                }
            }

            super.paintComponent(g);
        }

        private int countLines(JTextArea textArea) {
            AttributedString text = new AttributedString(textArea.getText());
            FontRenderContext frc = textArea.getFontMetrics(textArea.getFont())
                    .getFontRenderContext();
            AttributedCharacterIterator charIt = text.getIterator();
            LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(charIt, frc);
            float formatWidth = (float) textArea.getSize().width;
            lineMeasurer.setPosition(charIt.getBeginIndex());

            int noLines = 0;
            while (lineMeasurer.getPosition() < charIt.getEndIndex()) {
                lineMeasurer.nextLayout(formatWidth);
                noLines++;
            }

            return noLines;
        }
    }


    /**
     * This class is used to make the JTextArea lines wrap every time the window
     * is resized. Without this, the JTextArea lines will not shrink back if the
     * parent window shrinks. This is achieved by returning true on getScrollableTracksViewportWidth();
     */
    private class VerticalScrollPane extends JPanel implements Scrollable {

        private static final long serialVersionUID = 7477168367035025136L;

        public VerticalScrollPane() {
            this(new GridLayout(0, 1));
        }

        public VerticalScrollPane(LayoutManager lm) {
            super(lm);
        }

        public VerticalScrollPane(Component comp) {
            this();
            add(comp);
        }

        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return getPreferredSize();
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect,
                int orientation, int direction) {
            return 10;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect,
                int orientation, int direction) {
            return 100;
        }

        @Override
        public boolean getScrollableTracksViewportWidth() {
            return true;
        }

        @Override
        public boolean getScrollableTracksViewportHeight() {
            return false;
        }
    }

    /**
     * Class structure for storing a single chat entry in a full conversation.
     */
    public static class ChatEntry {
        public String name;
        public String content;

        // For type 0=sent, 1=received.
        public int type;

        public ChatEntry(String name, String content, int type) {
            this.name = name;
            this.content = content;
            this.type = type;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // Simulate a conversation between two users and pass it on to the
                // chat window to test the UI.
                List<ChatEntry> chatContentList = new LinkedList<>();
                chatContentList.add(new ChatEntry("David", "Hey Lori, how are you?", 0));
                chatContentList.add(new ChatEntry("Lori", "Hi David, I'm good. What have you been up to?", 1));
                chatContentList.add(new ChatEntry("David", "I've been super busy with work.", 0));
                chatContentList.add(new ChatEntry("David", "Haven't had much free time to even go out to eat.", 0));
                chatContentList.add(new ChatEntry("Lori", "I know what you mean, I've had to work on projects after projects.", 1));
                chatContentList.add(new ChatEntry("David", "Let's make some time and go to lunch tomorrow!", 0));
                chatContentList.add(new ChatEntry("Lori", "That sounds great, let's do 12pm. I know a great food truck by my building.", 1));
                chatContentList.add(new ChatEntry("David", "Perfect, I'll meet you at the entrance of your building.", 0));
                chatContentList.add(new ChatEntry("Lori", "Awesome, see you tomorrow :)", 1));
                new ChatWindow(chatContentList);
            }
        });
    }

}
韩经武
2023-03-14

调整窗口大小时,重新显示文本区域是一种耗时的方法。我已经为每个聊天条目添加了ComponentListeners,这将调整条目TextArea的大小。这只是概念的证明,可以在性能方面加以改进。

尝试以下操作并减小窗口宽度,然后慢慢加宽以查看效果。它类似于您想象的目标。当窗口打开时,我还没有设法立即实现该布局。虽然慢慢加宽窗口效果很好,但如果您移动得太快,那么由于JDK代码中的缓存,线条可能会显得太宽。

无论如何,我想我分享了我的实验,也许它可以帮助您找到处理它的方法。我假设您可能必须实现自己的布局管理器来完全实现您的既定目标并绕过观察到的缓存。

计算TextArea内行数的方法取自另一个StackOverflow问题:如何计算JTextArea中的行数,包括由包装引起的行数?

public JPanel buildChatUI(List<ChatEntry> chatContentList) {
    final JPanel chatPanel = new JPanel();
    chatPanel.setLayout(new GridBagLayout());
    chatPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

    GridBagConstraints gbc = new GridBagConstraints();

    for (ChatEntry chatEntry : chatContentList) {
        JLabel nameLabel = new JLabel(chatEntry.name);

        final JTextArea contentTextArea = new JTextArea();
        contentTextArea.setText(chatEntry.content);
        contentTextArea.setOpaque(true);
        contentTextArea.setLineWrap(true);
        contentTextArea.setWrapStyleWord(true);
        contentTextArea.setEditable(false);
        chatPanel.addComponentListener(new ComponentListener() {
            @Override
            public void componentResized(ComponentEvent e) {
                int lc = countLines(contentTextArea);
                GridBagLayout gbl = (GridBagLayout) chatPanel.getLayout();
                GridBagConstraints constraints = gbl.getConstraints(contentTextArea);
                if (lc == 1) {
                    if (constraints.fill == GridBagConstraints.HORIZONTAL) {
                        constraints.fill = GridBagConstraints.NONE;
                        gbl.setConstraints(contentTextArea, constraints);
                    }
                } else {
                    if (constraints.fill == GridBagConstraints.NONE) {
                        constraints.fill = GridBagConstraints.HORIZONTAL;
                        gbl.setConstraints(contentTextArea, constraints);
                    }
                }
            }
            @Override public void componentMoved(ComponentEvent e) { }
            @Override public void componentShown(ComponentEvent e) { }
            @Override public void componentHidden(ComponentEvent e) { }
        });

        // Arrange each chat entry based on the user.
        if (chatEntry.type == 1) {
            contentTextArea.setBackground(Color.YELLOW);
            gbc.anchor = GridBagConstraints.WEST;
        }
        else {
            contentTextArea.setBackground(Color.CYAN);
            gbc.anchor = GridBagConstraints.EAST;
        }

        gbc.insets.set(0, 0, 0, 0);
        gbc.weightx = 1.0;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.fill = GridBagConstraints.NONE;
        chatPanel.add(nameLabel, gbc);
        gbc.fill = GridBagConstraints.HORIZONTAL;
        if (gbc.anchor == GridBagConstraints.WEST) {
            gbc.insets.set(0, 0, 0, 40);
            chatPanel.add(contentTextArea, gbc);
        }
        else {
            gbc.insets.set(0, 40, 0, 0);
            chatPanel.add(contentTextArea, gbc);
        }
    }

    return chatPanel;
}

/**
 * From https://stackoverflow.com/questions/6366776/how-to-count-the-number-of-lines-in-a-jtextarea-including-those-caused-by-wrapp
 * @param textArea the text area of interest
 * @return number of lines in text area
 */
private static int countLines(JTextArea textArea) {
    AttributedString text = new AttributedString(textArea.getText());
    FontRenderContext frc = textArea.getFontMetrics(textArea.getFont())
            .getFontRenderContext();
    AttributedCharacterIterator charIt = text.getIterator();
    LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(charIt, frc);
    float formatWidth = (float) textArea.getSize().width;
    lineMeasurer.setPosition(charIt.getBeginIndex());

    int noLines = 0;
    while (lineMeasurer.getPosition() < charIt.getEndIndex()) {
        lineMeasurer.nextLayout(formatWidth);
        noLines++;
    }

    return noLines;
}
 类似资料:
  • 在Swing应用程序中,我有一个使用GridBagLayout的面板。在这里,我有3列,其中一列中有一个JTextField。我想要的是设置这个JTextField的宽度(使用setPreferredSize()方法),以便它占用其列的所有可用空间。 据我所知,列的宽度是根据我使用GridBagConstraint分配给它们的权重计算的。我试图从面板的宽度中减去同一行其他组件的宽度之和,但这似乎不

  • 问题内容: 我有这个设置: HTML : CSS : 通过此设置,它可以在iPhone上运行,但不能在浏览器中运行。 是因为我已经在meta中,也许已经在? 问题答案: 我发现最好的方法是为较旧的浏览器编写默认CSS,因为较旧的浏览器包括5.5、6、7和8。无法读取@media。当我使用@media时,我会这样使用: 但是,您可以使用@media进行任何操作,这只是为所有浏览器构建样式时最适合我的

  • 问题内容: 使用CSS媒体查询,您可以用于定位设备宽度(例如iPhone或Android设备)和/或定位页面宽度的。 如果使用,则在更改桌面上浏览器窗口的大小时,CSS不会更改,因为您的桌面不会更改大小。 如果使用,则在更改桌面上浏览器窗口的大小时,可能会显示面向移动的样式,例如触摸友好的元素和菜单等。 现在已不再针对特定的浏览器(和设备?)定位,并且您应该对目标对象有所了解。那也适用于媒体查询吗

  • 问题内容: 在下面的HTML片段中,如何使包含“ LAST”的列的宽度占据行的其余部分,并且包含“ COLUMN ONE”和“ COLUMN TWO”的列的宽度足够宽以包含其内容,而不是更大。 谢谢 问题答案: 您将需要告诉前两列不要换行,并为最后一列提供99%的宽度: 编辑: 您应该将所有样式/演示文稿放在(外部…)css中。 对列使用类(如果您仅针对现代浏览器,则可以使用css3选择器,例如)

  • 问题内容: 我想在不创建换行符的情况下最大化“搜索”文本输入的宽度(即,“清除”链接将与“关于”链接紧密结合)。 我只有CSS和网页设计的基本经验。我阅读了有关“ ”技巧的内容,但是当目标元素右边有更多元素时,我不明白如何使用它。 谢谢 编辑: 可以部分解决方案是针对每种情况“手动”设置宽度,例如在: 但是当菜单的文本为“动态”(例如,包含“ Hello用户名”)时,此解决方案将不起作用。 我想如

  • 我觉得我需要稍微重新表述一下这个问题。 更新问题如下。 我有一个包含: 它包含以下三个面板: 具有固定大小“x”和“y”的J面板 没有固定大小的JPanel 没有固定尺寸和小高度的J面板 第二个JGroup包含一个JTable,因此它会扩展以填充整个高度,并按预期将底部面板一直向下推。 像这样: t=顶部面板,m=中间面板,b=底部面板。 这很有效。但底部面板感觉不到填充整个父对象的宽度,这是一个