JTextArea resizes unexpectedly when resizing dialog box

I have a problem using JTextArea . My actual setup is different, but the effects remain. Here is a picture of the problem:

enter image description here

When the owner of JDialog resizes just 1 pixel below what JTextArea requires for their preferred sizes, text areas change unexpectedly. In my real setup, they suddenly grow tall. I use GridBagLayout , but it seems to be happening in other layouts. Why is this?

Here is a simple compiled code to reproduce the above effect.

 import java.awt.*; import java.awt.event.*; import java.text.SimpleDateFormat; import java.util.Date; import javax.swing.*; import javax.swing.text.JTextComponent; public class TextDemo extends JDialog implements ActionListener { private static final long serialVersionUID = -589374238138963529L; protected JTextField textField; protected JTextArea textArea; private final static String newline = "\n"; private static final java.awt.Dimension SCREENSIZE = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); private static final java.awt.Point SCREENCENTER = new java.awt.Point(SCREENSIZE.width/2,SCREENSIZE.height/2); public TextDemo(Window owner, String shortMessage, String message, JComponent accessory) { super(owner); setTitle("Test"); setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE); Icon icon = UIManager.getIcon("OptionPane.warningIcon"); JTextArea shortText = makeMultiLineLabel(true); shortText.setBorder(BorderFactory.createEtchedBorder()); shortText.setFont(shortText.getFont().deriveFont(Font.BOLD)); shortText.setRows(2); shortText.setColumns(20); shortText.setText(shortMessage); JTextArea messageText = makeMultiLineLabel(true); messageText.setBorder(BorderFactory.createEtchedBorder()); messageText.setFont(shortText.getFont().deriveFont(Font.PLAIN)); messageText.setRows(4); messageText.setColumns(20); messageText.setText(message); JPanel buttonPanel = new JPanel(); buttonPanel.add(new JButton("OK")); buttonPanel.add(new JButton("Cancel")); JPanel contentPanel = new JPanel(); contentPanel.setOpaque(true); contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 8, 9)); contentPanel.setLayout(new GridBagLayout()); GridBagConstraints c; c = new GridBagConstraints(); c.gridx = 0; c.gridy = 0; c.anchor = GridBagConstraints.FIRST_LINE_START; c.gridheight = 2; contentPanel.add(new JLabel(icon), c); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 0; c.fill = GridBagConstraints.BOTH; c.weighty = 1.0; c.weightx = 1.0; contentPanel.add(shortText, c); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 1; c.fill = GridBagConstraints.BOTH; c.weighty = 1.0; c.weightx = 1.0; contentPanel.add(messageText, c); if (accessory != null) { c = new GridBagConstraints(); c.gridx = 0; c.gridy = 2; c.gridwidth = 2; c.fill = GridBagConstraints.BOTH; c.weighty = 1.0; c.weightx = 1.0; contentPanel.add(accessory, c); } c = new GridBagConstraints(); c.gridwidth = 2; c.gridx = 0; c.gridy = 3; contentPanel.add(buttonPanel, c); setContentPane(contentPanel); } public void actionPerformed(ActionEvent evt) { String text = textField.getText(); textArea.append(text + newline); textField.selectAll(); //Make sure the new text is visible, even if there //was a selection in the text area. textArea.setCaretPosition(textArea.getDocument().getLength()); } /** * Create the GUI and show it. For thread safety, * this method should be invoked from the * event dispatch thread. */ private static void createAndShowGUI() { //Create and set up the window. JFrame frame = new JFrame("TextDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); try { throw new Exception("Test"); } catch (Exception e) { TextDemo t = new TextDemo(frame, "You won't get away with this!", "Alert! Alert! A chocy nut bar has been removed without payment!" + " A chocy nut bar... has been REMOVED! WITHOUT PAYMENT! Alert, alert!", getStackTraceTextArea(e)); //Display the window. frame.pack(); frame.setLocation(SCREENCENTER.x - frame.getSize().width/2, SCREENCENTER.y - frame.getSize().height/2); frame.setVisible(true); t.setModal(true); t.pack(); t.setLocation(getPos(t, t.getOwner())); t.setVisible(true); } } protected static JComponent getStackTraceTextArea(Throwable exception) { JTextArea textArea = new JTextArea(); textArea.setEditable(false); textArea.setLineWrap(false); textArea.append(getTraceMessage(exception)); textArea.setCaretPosition(0); JScrollPane scroll = new JScrollPane(textArea); scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); scroll.setPreferredSize(new Dimension(50, 140)); return scroll; } private static final String getTraceMessage(Throwable exception) { StringBuilder out = new StringBuilder((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")) .format(new Date())+": Unhandled Exception: \n" +exception.toString()+"\n\nStack Trace:\n"); StackTraceElement[] stackTrace = exception.getStackTrace(); for (int i = 0; i < stackTrace.length; i++) { String toAppend = stackTrace[i].toString(); if (i != stackTrace.length-1) toAppend += "\n"; out.append(toAppend); } return out.toString(); } public static final JTextArea makeMultiLineLabel(boolean selectable) { JTextArea area = new JTextArea(); area.setWrapStyleWord(true); area.setLineWrap(true); area.setFont(UIManager.getFont("Label.font")); area.setEditable(false); area.setCursor(null); area.setOpaque(false); area.setFocusable(selectable); area.setAlignmentX(JTextComponent.LEFT_ALIGNMENT); area.setMinimumSize(new Dimension(0,0)); return area; } private static Point getPos(JDialog d, Window w) { return new Point(w.getX()+(w.getWidth ()-d.getWidth ())/2, w.getY()+(w.getHeight()-d.getHeight())/2); } public static void main(String[] args) { //Schedule a job for the event dispatch thread: //creating and showing this application GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } } 

EDIT . With some changes made, the problem remains:

enter image description here

+4
source share
1 answer

The problem you are observing is GridBagLayout trying to cope with a situation where it is unable to execute the preferredSize component, it returns instead of using the minimumSize components ...

You can use the GridBagConstraints#weightx property to force the component to always fill the column width ...

 c = new GridBagConstraints(); c.gridx = 1; c.gridy = 0; c.fill = GridBagConstraints.BOTH; c.weighty = 1.0; c.weightx = 1.0; contentPanel.add(shortText, c); c = new GridBagConstraints(); c.gridx = 1; c.gridy = 1; c.fill = GridBagConstraints.BOTH; c.weighty = 1.0; c.weightx = 1.0; contentPanel.add(messageText, c); 

This will not stop it from being compressed, but it will stop it from “snapping” from one size to another.

Try and don’t use setPreferredSize , check Should I avoid using the methods of the Set method (Preferred | Maximum | Minimum) in Java Swing? for more information, use the rows and columns JTextArea ...

 shortText.setRows(2); // for example 

Personally, I would also JTextArea in a JScrollPane , then it becomes, a little, less important to have enough space for each

Feedback...

Now the question is out of context, but it seems to me that you will put a lot of effort into a small gain.

For example, you can use JOptionPane instead and take advantage of the Swing HTML rendering capabilities ...

enter image description here

 import java.awt.EventQueue; import javax.swing.JOptionPane; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class OptionPaneTest { public static void main(String[] args) { new OptionPaneTest(); } public OptionPaneTest() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } StringBuilder sb = new StringBuilder(128); sb.append("<html><b><p align=center>You won't get away with this!</p></b><br>"); sb.append("Alert! Alert! A chocy nut bar has been removed without payment!"); sb.append("<br>A chocy nut bar... has been REMOVED! WITHOUT PAYMENT! Alert, alert!"); JOptionPane.showMessageDialog(null, sb.toString(), "Alert", JOptionPane.WARNING_MESSAGE); } }); } } 

Also...

I think you will find ...

 frame.setLocationRelativeTo(null); 

Much easier and less time ...

 frame.setLocation(SCREENCENTER.x - frame.getSize().width / 2, SCREENCENTER.y - frame.getSize().height / 2); 

It also means that you can use t.setLocationRelativeTo(frame) as well ...

Oh, also +1 for a link with a red dwarf;)

Updated from updates to the question

The solution remains (basically) the same, use JTextArea#setRows and JTextArea#setColumns ...

Your code ...

enter image description here

My code ...

enter image description here

 JTextArea shortText = makeMultiLineLabel(true); shortText.setBorder(BorderFactory.createEtchedBorder()); shortText.setFont(shortText.getFont().deriveFont(Font.BOLD)); // FontMetrics fm = shortText.getFontMetrics( // shortText.getFont()); // shortText.setPreferredSize(new Dimension( // Math.min(fm.stringWidth(shortMessage), 300), // fm.getHeight())); shortText.setRows(2); shortText.setColumns(20); shortText.setText(shortMessage); JTextArea messageText = makeMultiLineLabel(true); messageText.setBorder(BorderFactory.createEtchedBorder()); messageText.setFont(shortText.getFont().deriveFont(Font.PLAIN)); // fm = messageText.getFontMetrics( // messageText.getFont()); // messageText.setPreferredSize(new Dimension( // Math.min(fm.stringWidth(message), 300), // fm.getHeight())); messageText.setRows(4); messageText.setColumns(20); messageText.setText(message); 

You can also watch SwingX JXErrorDialog as well

+5
source

Source: https://habr.com/ru/post/1498513/


All Articles