How can I make my panel wrap text and increase height instead of width when contained in a GridBagLayout?

[FINAL IMAGE]

Oh people. I don't know why this doesn't work in my chat program. But I was able to fix sscce .. with a scrollable implementation where it works now. Thank you all! Code:

package de.sky.cjat;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.Scrollable;
import javax.swing.UIManager;

public class MyKskb {

    private int row;

    class SomethingPanel extends JPanel implements Scrollable {

        public SomethingPanel(LayoutManager manager){
            super(manager);
        }

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

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

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

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

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

    public MyKskb(){
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setPreferredSize(new Dimension(800, 600));
//      final JPanel content = new JPanel(new GridBagLayout());
        final SomethingPanel content = new SomethingPanel(new GridBagLayout());

//      final JPanel empty = new JPanel();
//      empty.setPreferredSize(new Dimension(0, 0));
//      GridBagConstraints gbc = new GridBagConstraints();
//      content.add(empty, gbc);
        content.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "§");
        content.getActionMap().put("§", new AbstractAction(){
            @Override
            public void actionPerformed(ActionEvent e){
                JPanel p = new JPanel(new BorderLayout());
                p.setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
                p.setBackground(Color.GRAY);

                GridBagConstraints gbc = new GridBagConstraints();
                gbc.insets = new Insets(10, 10, 10, 10);
                gbc.weightx = 1.0;
                gbc.gridx = 0;
                gbc.gridy = row++;
                gbc.fill = GridBagConstraints.HORIZONTAL;

//              content.remove(empty);
//              gbc.gridy = row;
//              gbc.weighty = 1.0;
//              content.add(empty, gbc);

                JTextArea text = new JTextArea(
                        Math.random() > 0.5 ? 
                        "ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC" : 
                        "FFFFFFFFFFFFABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC");
                text.setLineWrap(true);
                p.add(text);
                content.add(p, gbc);
                content.revalidate();
            }
        });
        JScrollPane scrollPane = new JScrollPane(content);
        f.pack();
        f.setLocationRelativeTo(null);
        f.add(scrollPane);
        f.setVisible(true);
    }

    public static void main(String[] args) throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        new MyKskb();
    }
}

      

[END]

[Question]

I am writing a chat program and the layout looks like this:

enter image description here

The message pane has a gridbaglayout and is wrapped in a JScrollPane. The problem is that if the message is too long, the message bar stretches and becomes too large:

enter image description here

So what I want to achieve is to limit the size of the pane that is wrapped in a jscrollpane - I want the gray message text panes to increase their height if the text gets longer rather than wider. That is, wrap the text without increasing the width of the panel.

Is there a way to achieve this with the ghe grid layout I am missing something? Or should I use a different layout manager?

Edit: Full hierarchy:

JScrollPane
    JPanel (gbl)
        JPanel (custom painting)
            JComponent (JLabel but it can vary)
        JPanel (custom painting)
            JComponent (JLabel but it can vary)
        JPanel (custom painting)
            JComponent (JLabel but it can vary)
        ....    

      

edit: I tried to implement a scrollable like this:

@Override
    public Dimension getPreferredScrollableViewportSize(){
        return new Dimension(100, getHeight());
    }

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

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

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

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

      

but the same happens if the text is too long ... I don't know what getPreferredScrollableViewportSize should get, but also getSize () did'nt work ...

EDIT: here is the sscce, it shows the basic structure of my gui: (press enter to add a new component)

package de.sky.cjat;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import javax.swing.UIManager;

public class Kskb {

    private int row;

    public Kskb(){
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setPreferredSize(new Dimension(800, 600));
        final JPanel content = new JPanel(new GridBagLayout());
        final JPanel empty = new JPanel();
        empty.setPreferredSize(new Dimension(0, 0));
        GridBagConstraints gbc = new GridBagConstraints();
        content.add(empty, gbc);
        content.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "§");
        content.getActionMap().put("§", new AbstractAction(){
            @Override
            public void actionPerformed(ActionEvent e){
                JPanel p = new JPanel();
                p.setBackground(Color.GRAY);
                JLabel text = new JLabel("ABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC");
                p.add(text);

                GridBagConstraints gbc = new GridBagConstraints();
                gbc.insets = new Insets(10, 10, 10, 10);
                gbc.weightx = 1.0;
                gbc.gridy = row++;
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                gbc.anchor = GridBagConstraints.NORTHWEST;

                content.add(p, gbc);

                content.remove(empty);
                gbc.gridy = row;
                gbc.weighty = 1.0;
                content.add(empty, gbc);

                content.revalidate();
            }
        });
        JScrollPane scrollPane = new JScrollPane(content);
        f.pack();
        f.setLocationRelativeTo(null);
        f.add(scrollPane);
        f.setVisible(true);
    }

    public static void main(String[] args) throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        new Kskb();
    }
}

      

+3


source to share


4 answers


Following @durandal's recommendation, the problem is in the JLabel. JLabel does not wrap text. Therefore, you need to use a different component. Here is your code modified with JTextArea:

import java.awt.*;
import java.awt.event.ActionEvent;

import javax.swing.*;
import javax.swing.border.*;

public class Kskb {

    private int row;

    public Kskb()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().setPreferredSize(new Dimension(800, 600));
//        final JPanel content = new JPanel(new GridBagLayout());
        final ScrollablePanel content = new ScrollablePanel(new GridBagLayout());
        content.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );
        final GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(10, 10, 10, 10);
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        content.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "");
        content.getActionMap().put("", new AbstractAction(){
            @Override
            public void actionPerformed(ActionEvent e)
            {
                JPanel p = new JPanel( new BorderLayout() );
                p.setBorder( new EmptyBorder(10, 10, 10, 10) );
                p.setBackground(Color.GRAY);
                JTextArea text = new JTextArea();
                text.append("ABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABCABCABCABCABC ABCABCABCABCABCABCABCABCABCABCABCABCABCA BCABCABCABCABCABCABCAB CABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABCABC");
                text.setLineWrap( true );
                p.add(text);

                gbc.gridy = row++;
                content.add(p, gbc);
                content.revalidate();
            }
        });

        JScrollPane scrollPane = new JScrollPane(content);
        f.pack();
        f.setLocationRelativeTo(null);
        f.add(scrollPane);
        f.setVisible(true);
    }

    public static void main(String[] args) throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        new Kskb();
    }
}

      



The key is an implementation Scrollable

that makes the width of the panels adjust when the frame is adjusted. This code uses the Scrollable Panel , which is just a reusable interface implementation Scrollable

that you can customize with methods instead of creating a new class and overriding the methods.

+1


source


This is the interaction effect between the JScrollPane and its contents. The scrolling area (default) provides as much space as the content wants.

So, in that sense, the layout works fine.

In order to expose the component shown in the scrollbar to actually control the size of the container it shows, the contained component can implement the interface javax.swing.Scrollable

.



Try something like this:

public class MyVerticalPanel extends JPanel implements Scrollable {

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

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

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

    // other method and implementation
}

      

More documentation is available here: https://docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html#scrollable

+1


source


I managed to get something working according to what you want:

public class Chatter extends JFrame {

    JTextField enterTextField = new JTextField("This is a lot of text it way too loooooooooooooooong to fit in one line but it is used to check for wrapping by words and characters some more text just for fun no punctuation");
    JPanel historyPanel = new JPanel();

    public Chatter() {

        enterTextField.addActionListener(new enterTextActionListener());

        historyPanel.setLayout(new BoxLayout(historyPanel, BoxLayout.PAGE_AXIS));
        JScrollPane scrollPane = new JScrollPane(historyPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

        getContentPane().add(enterTextField, BorderLayout.PAGE_END);
        getContentPane().add(scrollPane, BorderLayout.CENTER);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(300, 300);
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private class enterTextActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {

            JTextArea textArea = new JTextArea(enterTextField.getText());
            textArea.setLineWrap(true);
            textArea.setWrapStyleWord(true);

            JScrollPane scrollPane = new JScrollPane(textArea);
            scrollPane.setBorder(BorderFactory.createLineBorder(Color.RED, 5));
            scrollPane.setPreferredSize(textArea.getPreferredSize());   

            historyPanel.add(scrollPane);
            historyPanel.add(Box.createRigidArea(new Dimension(0,10)));
            historyPanel.revalidate();
//          enterTextField.setText(""); // Commented out for convenience while testing.
        }
    }

    public static void main(String args[]) {

        new Chatter();
    }
}

      

Explanation:

I am using JTextArea

to display text since it supports text anchor. Each text area is inside JScrollPane

to allow vertical scrolling as a last resort if there is not enough space to display the text.

Notes:

  • I used JTextField

    as text input method, you can use multi-line text component instead.
    • The source code is for testing purposes only.
    • The text is not cleared with each entry for testing (see the commented line in the code).
  • I used BoxLayout

    for a panel that contains text messages because it felt simpler and more natural. GridBagLayout

    could have been used instead.
  • I installed setWrapStyleWord(true)

    because this is probably the correct behavior for word wrapping. If you want to wrap with characters, remove that line.
  • I've added a border and spaces for testing. Use whatever style you want instead.
+1


source


I think you can do this.

Jpanel.setPreferredSize(new Dimension(x size, y size));
Jframe.add(Jpanel);

      

I think most layout managers work better with setPreferredSize than setSize.

0


source







All Articles