Get the real size of a JFrame content - java

I get a JFrame and i want to display a JLabel with a border in it with a padding of maybe 50px. When i set the size of the JFrame to 750, 750, and the size of the JLabel to 650, 650 and the location to 50, 50, it display it strange... Here's my code:
public class GUI {
/**
* Declarate all
*/
public int height = 750;
public int width = 750;
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
int x = (screen.width / 2) - (width / 2); // Center horizontally.
int y = (screen.height / 2) - (height / 2); // Center vertically.
/**
* Create the GUI
*/
JFrame frame = new JFrame();
Border border = LineBorder.createBlackLineBorder();
JLabel label = new JLabel();
public GUI(){
label.setBorder(border);
label.setSize(700, 700);
label.setLocation(0, 0);
frame.getContentPane().setLayout(null);
frame.add(label);
}
public void createGUI() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(x,y,width,height);
frame.setVisible(true);
}
}
So I think the title-bar at the top is also include in the size. In Graphics you can use getInsets(). Now is there anything like that for Swing / JFrame?

First get the pixels trimmed out by the frame.
int reqWidth = reqHeight = 750;
// first set the size
frame.setSize(reqWidth, reqHeight);
// This is not the actual-sized frame. get the actual size
Dimension actualSize = frame.getContentPane().getSize();
int extraW = reqWidth - actualSize.width;
int extraH = reqHeight - actualSize.height;
// Now set the size.
frame.setSize(reqWidth + extraW, reqHeight + extraH);
An alternate simpler way. The previous works but this is recommended.
frame.getContentPane().setPreferredSize(750, 750);
frame.pack();
Hope this helps.
EDIT:
Add this in your constructor before adding components to the frame. and to set it in the middle, use
frame.setLocationRelativeTo(null);
This will center the window on the screen.

Using setPreferredSize() is problematic, as it always overrules the component's calculation with an arbitrary choice. Instead, pack() the enclosing Window to accommodate the preferred sized of the components, as shown below.
import java.awt.Color;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
/**
* #see https://stackoverflow.com/a/13481075/230513
*/
public class NewGUI extends JPanel {
private static final int S1 = 10;
private static final int S2 = 50;
private JLabel label = new JLabel("Hello, world!");
public NewGUI() {
label.setHorizontalAlignment(JLabel.CENTER);
Border inner = BorderFactory.createEmptyBorder(S1, S1, S1, S1);
Border outer = BorderFactory.createLineBorder(Color.black);
label.setBorder(new CompoundBorder(outer, inner));
this.setBorder(BorderFactory.createEmptyBorder(S2, S2, S2, S2));
this.add(label);
}
private void display() {
JFrame f = new JFrame("NewGUI");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new NewGUI().display();
}
});
}
}

Related

JTextField width according to max input, calculating string width

I would like to have a TextField which shows by its width the maximum of allowed
input characters.
I used Font.getStringBounds to calculate the width of the maximum length.
But to my surprise the resulting width in the example looks like omitting a
complete character!
Using FontMetrics.stringWidth supplies the same width value.
Creating the textField just using the JTextField(int columns) constructor gave
a better result, but the field is still too small.
What is missing?
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
public class InputWidthTextField extends JFrame {
public InputWidthTextField() {
setSize(250, 230);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new FlowLayout(FlowLayout.CENTER, 60, 20));
Font font= new Font(Font.MONOSPACED, Font.PLAIN, 12);
int max= 4;
JLabel lb= new JLabel("Enter up to "+max+" characters:");
add(lb);
MaxInputWidthTextField tf= new MaxInputWidthTextField(font, max);
add(tf);
setVisible(true);
}
public static void main(String... args) {
EventQueue.invokeLater(InputWidthTextField::new);
}
class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super();
if (font==null)
font= getFont();
else
setFont(font);
FontMetrics fm= getFontMetrics(font);
FontRenderContext frc= fm.getFontRenderContext();
String buf= "8".repeat(maxCount);
Rectangle2D rect= font.getStringBounds(buf, frc);
Dimension dim= new Dimension((int)rect.getWidth(), (int)rect.getHeight());
setPreferredSize(dim);
System.out.println((int)rect.getWidth()+", "+(int)rect.getHeight());
System.out.println(fm.stringWidth(buf));
System.out.println(getGraphics());
}
}
}
I'm not even going to try and figure out all the things that are wrong with your code, instead, I'm going to demonstrate what you should be doing instead.
Take a look at public JTextField​(int columns)
JTextFieldpublic JTextField​(int columns) Constructs a new empty
TextField with the specified number of columns. A default model is
created and the initial string is set to null. Parameters: columns -
the number of columns to use to calculate the preferred width; if
columns is set to zero, the preferred width will be whatever naturally
results from the component implementation
So, if we do a side by side comparison, this is what we get (your field is on the bottom)
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JTextField(8), gbc);
add(new MaxInputWidthTextField(null, 8), gbc);
}
}
class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super();
if (font == null) {
font = getFont();
} else {
setFont(font);
}
FontMetrics fm = getFontMetrics(font);
FontRenderContext frc = fm.getFontRenderContext();
String buf = "8".repeat(maxCount);
Rectangle2D rect = font.getStringBounds(buf, frc);
Dimension dim = new Dimension((int) rect.getWidth(), (int) rect.getHeight());
setPreferredSize(dim);
}
}
}
What I do recommend is having a look at the pre-existing implementation
The JTextField#getPreferredSize implementation looks like...
/**
* Returns the column width.
* The meaning of what a column is can be considered a fairly weak
* notion for some fonts. This method is used to define the width
* of a column. By default this is defined to be the width of the
* character <em>m</em> for the font used. This method can be
* redefined to be some alternative amount
*
* #return the column width >= 1
*/
protected int getColumnWidth() {
if (columnWidth == 0) {
FontMetrics metrics = getFontMetrics(getFont());
columnWidth = metrics.charWidth('m');
}
return columnWidth;
}
/**
* Returns the preferred size <code>Dimensions</code> needed for this
* <code>TextField</code>. If a non-zero number of columns has been
* set, the width is set to the columns multiplied by
* the column width.
*
* #return the dimension of this textfield
*/
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if (columns != 0) {
Insets insets = getInsets();
size.width = columns * getColumnWidth() +
insets.left + insets.right;
}
return size;
}
Instead of "setting" the preferred size, I would "consider" overriding the getPreferredSize and injecting your "custom" workflow into instead
Further experimentation...
So, I did a really quick test...
Font monoFont = new Font(Font.MONOSPACED, Font.PLAIN, 13);
JTextField field = new JTextField();
field.setFont(monoFont);
FontMetrics metrics = field.getFontMetrics(getFont());
for (int i = 32; i < 127; i++) {
System.out.println(i + " " + ((char) i) + " = " + metrics.charWidth((char)i));
}
And found that every character is 8 points wide. Where as the default font uses the character m which is (in my testing) 12 points wide.
So, this got me to thinking, the really problem isn't that the getPreferredSize is wrong, it's that we actually need to "pad" the result of getColumnWidth, for example...
public class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super(maxCount);
setFont(font);
}
#Override
protected int getColumnWidth() {
return super.getColumnWidth() + 1;
}
}
which can generate something like...
It's just enough to add a little white space at the trailing end of the text field and you don't need to jump through a lot of hopes to make it work.
Runnable example
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
Font monoFont = new Font(Font.MONOSPACED, Font.PLAIN, 13);
add(new JTextField(8), gbc);
add(new MaxInputWidthTextField(monoFont, 8), gbc);
}
}
public class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super(maxCount);
setFont(font);
}
#Override
protected int getColumnWidth() {
return super.getColumnWidth() + 1;
}
}
}
#Camickr
Rob, you seem to have been very strict yesterday. ;-)
But surely, not understanding is also on my side. For when I tell you that in a monospaced font both "W" and "i" have the same width, this will certainly be nothing new to you. So I don't understand why you are asking me for an MRE or why only "WWWW" didn't work for you. I can only imagine, you didn't use a monospace font,
but this is a sine qua non when calculating the maximum possible width beforehand, given a maximum character count, for any string the user will enter.
I hope, we don't see different things on our screens due to resolution or scaling differences. So I send a picture of how it looks like at my site. Whenever I enter the last character in one of the textfields, the whole string is shifted to the left, so that the first character gets partly hidden. Hence in my example the zero is always cut. You may deem this to be acceptable, but I would prefer to have each character fully visible.
import java.awt.*;
import javax.swing.*;
public class FieldWidthTest extends JFrame {
public FieldWidthTest() {
setSize(250, 230);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new GridLayout(4, 1));
Font font= new Font(Font.MONOSPACED, Font.PLAIN, 12);
int max= 4;
for (int i=0; i<4; i++) {
JPanel p= new JPanel();
JTextField tf= new JTextField(max++);
tf.setFont(font);
p.add(tf);
add(p);
}
setVisible(true);
}
public static void main(String... args) {
EventQueue.invokeLater(FieldWidthTest::new);
}
}
#MadProgrammer
Overriding getPreferredSize(), as you suggested, worked well; but using setPreferredSize() also did the job and saves the overriding.
It was an error not to create and set a monospaced font in case the paramter was null. The solution, however, was to add 2 to the dimension's width, which worked for all font sizes. Camickr would call this justifiably "using magic numbers", but I don't know where to get this number from.
Thanks to all.
class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font monoFont, int maxCount) {
super();
if (monoFont==null) monoFont= new Font(Font.MONOSPACED, Font.PLAIN, 13);
setFont(monoFont);
FontMetrics fm= getFontMetrics(monoFont);
FontRenderContext frc= fm.getFontRenderContext();
String buf= "8".repeat(maxCount);
Rectangle2D rect= monoFont.getStringBounds(buf, frc);
// Insets insets= getMargin(); // Parameters are all 0.
Insets insets= getInsets();
int iWidth= insets.left + (int)rect.getWidth() + insets.right +2;
int iHeight= insets.top + (int)rect.getHeight() + insets.bottom;
Dimension dim= new Dimension(iWidth, iHeight);
setPreferredSize(dim);
}
}

How to resize a drawn oval in Java?

I need to resize a drawn oval in Java, I created this code for it:
FrameView.java
package tutorial;
import java.awt.*;
import javax.swing.*;
public class FrameView{
public static void main(String args[]){
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BallCreation c = new BallCreation();
f.add(c);
f.setSize(500, 500);
f.setVisible(true);
}
}
BallCreation.java
package tutorial;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BallCreation extends JPanel{
private static final long serialVersionUID = 1L;
private int height = 10;
private int width = 10;
private JPanel panel;
private JButton button1;
public BallCreation(){
panel = new JPanel();
button1 = new JButton("Click");
add(button1);
button1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
height = height + 2;
width = width + 2;
}
});
}
public void paintComponent(Graphics g){
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.setColor(Color.GREEN);
g.fillOval(10, 10, width, height);
}
}
The problem is is that it isn't working, I am not sure how I can make the oval refresh to its new size. I think it should be working, but for some reason the button doesn't parse the new height and width on to the paintComponent.
Just add a repaint() at the end of your actionPerformed method , or you won't see the change (unless you minimize then restore your window for instance, to force a repaint of the area).
button1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
height = height + 2;
width = width + 2;
repaint();
}
});

How to add a scrollbar for long JComponents in a JPanel? How to center a JComponent?

Currently, I have to JComponents contained in a JPanel with a vertical box layout.
This way, I can have the first component centered, as shown below, and have the bottom component (which is quite long) below. However, since the bottom component is very long I wanted to add a slider just for that specific component. This way, the user can see all of the bottom component with the upper component remaining centered. However, my code below doesn't fix anything and the scrollbar never even works. The only information about GPComponent and GPinfinity you need to know is they override the preferredSize, minimumSize, maximumSize, and paintComponent methods (they extend JComponent).
JFrame frame = new JFrame();
JPanel panel = new JPanel();
GPComponent gp = new GPComponent(n, k);
GPinfinityComponent gpi = new GPinfinityComponent(n, k);
Box box = new Box(BoxLayout.Y_AXIS);
panel.add(Box.createVerticalGlue());
panel.add(gp);
panel.add(Box.createVerticalGlue());
JScrollPane thePane = new JScrollPane(gpi, JScrollPane.VERTICAL_SCROLLBAR_NEVER,JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
panel.add(thePane);
frame.pack();6
frame.add(panel, BorderLayout.CENTER); // just to be clear
frame.setVisible(true);
final int FRAME_WIDTH = 600;
final int FRAME_HEIGHT = 600;
frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("GP("+n+", "+k+")");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
Also: the maximumSize=minimumSize=preferredSize for both components
For the circular one the dimensions are (350, 350) and for the other the dimensions are (5000, 150).
You state:
...and for the other the dimensions are (5000, 150).
If this is the component that is supposed to show the scrollbars, the Java is telling you otherwise, that it is in fact much shorter than you suppose it to be. I wonder if you're setting size instead of preferredSize. You actually should not be setting either but rather should override getPreferredSize() and have it return a dimension appropriate for the component.
For more detailed help, consider creating and posting a minimal example program.
Edit
For example, my MCVE:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class PreferredSizeEg extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 600;
public PreferredSizeEg() {
JPanel centerPanel = new JPanel(new GridBagLayout());
centerPanel.add(new CenterImagePanel());
JScrollPane scrollpane = new JScrollPane(new LongImagePanel(),
JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
setLayout(new BorderLayout());
add(centerPanel, BorderLayout.CENTER);
add(scrollpane, BorderLayout.PAGE_END);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private class LongImagePanel extends JPanel {
private static final int LI_PREF_W = 5000;
private static final int LI_PREF_H = 150;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int index = 0;
int spriteWidth = 50;
while ((index) * spriteWidth < getWidth()) {
Color c = index % 2 == 0 ? Color.green : Color.red;
g.setColor(c);
int x = 2 + index * spriteWidth;
int y = 2;
int width = getHeight() - 4;
int height = width;
g.fillOval(x, y, width, height);
index++;
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(LI_PREF_W, LI_PREF_H);
}
}
private class CenterImagePanel extends JPanel {
private static final int CIP_PREF_W = 200;
private static final int CIP_PREF_H = CIP_PREF_W;
public CenterImagePanel() {
setBorder(BorderFactory.createLineBorder(Color.BLACK));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.green);
int x = 5;
int y = x;
int width = getWidth() - 2 * x;
int height = getHeight() - 2 * y;
g.fillOval(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(CIP_PREF_W, CIP_PREF_H);
}
}
private static void createAndShowGui() {
PreferredSizeEg mainPanel = new PreferredSizeEg();
JFrame frame = new JFrame("PreferredSizeEg");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Which displays as:

How to auto-adjust font size of multiple JLabel based on container size in a smooth way?

I need to resize the font of multiple JLabel based on the scaling factor used to resize the container. To do this, I am setting the font of each JLabel to null so that they take the font of the container. It works, but it also produces strange results.
To be specific, the text seems to "lag" behind the container and sometimes it gets even truncated. I would like to avoid this behavior. Any idea how?
Example code simulating the behavior:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.geom.AffineTransform;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TextResize implements Runnable {
public static void main(String[] args) {
TextResize example = new TextResize();
SwingUtilities.invokeLater(example);
}
public void run() {
JFrame frame = new JFrame("JLabel Text Resize");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(800, 400));
Container container = frame.getContentPane();
container.setLayout(new BorderLayout());
final JPanel labelContainer = new JPanel(new GridBagLayout());
labelContainer.setBorder(BorderFactory.createLineBorder(Color.black));
//initial font
final Font textFont = new Font("Lucida Console", Font.PLAIN, 10).deriveFont(AffineTransform.getScaleInstance(1, 1));
labelContainer.setFont(textFont);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(0, 10, 0, 10);
c.weightx = 1;
for (int i = 0; i < 5; i++) {
JLabel f = new JLabel("Text here with possibly looooooooong words");
f.setBorder(BorderFactory.createLineBorder(Color.green));
f.setFont(null);//take the font from parent
c.gridy = i;
labelContainer.add(f, c);
}
JSlider slider = new JSlider(0,50000,10000);
slider.addChangeListener(new ChangeListener() {
double containerWidth = labelContainer.getPreferredSize().getWidth();
double containerHeight = labelContainer.getPreferredSize().getHeight();
#Override
public void stateChanged(ChangeEvent ev) {
JSlider source = (JSlider) ev.getSource();
double scale = (double) (source.getValue() / 10000d);
//scaling the container
labelContainer.setSize((int) (containerWidth * scale), (int) (containerHeight * scale));
//adjusting the font: why does it 'lag' ? why the truncation at times?
Font newFont = textFont.deriveFont(AffineTransform.getScaleInstance(scale, scale));
labelContainer.setFont(newFont);
//print (font.getSize() does not change?)
System.out.println(scale + " " + newFont.getTransform() + newFont.getSize2D());
}
});
container.add(slider, BorderLayout.NORTH);
JPanel test = new JPanel();
test.setLayout(null);
labelContainer.setBounds(5, 5, labelContainer.getPreferredSize().width, labelContainer.getPreferredSize().height);
test.add(labelContainer);
container.add(test, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
Picture:
http://i.stack.imgur.com/tZLOO.png
Thanks,
-s
You can use any of the following methods:
by #trashgod
by #StanislavL
by #coobird
I sort of solved the problem adding:
#Override
protected void paintComponent(Graphics g) {
final Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING, java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
super.paintComponent(g2d);
}
Thanks anyway.
If performance speed is an issue, then you might find the following information about the 3 methods pointed to by MKorbel above useful.
Coobird's code has some limitations if used on a multi-call basis (eg in a sizeChanged Listener or a LayoutManager)
Trashgod's method is between 2 and 4 times slower than Stanislav's (but it also is designed to fill the area BOTH directions as the OP asked in that question, so not unexpected.)
The code below improves on Stanislav's rectangle method (by starting from the current font size each time rather than reverting back to MIN_FONT_SIZE each time) and thus runs between 20 and 50 times faster than that code, especially when the window/font is large.
It also addresses a limitation in that code which only effectively works for labels located at 0,0 (as in the sample given there). The code below works for multiple labels on a panel and at any location.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
// Improved version of http://java-sl.com/tip_adapt_label_font_size.html
public class FontResizingLabel extends JLabel {
public static final int MIN_FONT_SIZE=3;
public static final int MAX_FONT_SIZE=240;
Graphics g;
int currFontSize = 0;
public FontResizingLabel(String text) {
super(text);
currFontSize = this.getFont().getSize();
init();
}
protected void init() {
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
adaptLabelFont(FontResizingLabel.this);
}
});
}
protected void adaptLabelFont(JLabel l) {
if (g==null) {
return;
}
currFontSize = this.getFont().getSize();
Rectangle r = l.getBounds();
r.x = 0;
r.y = 0;
int fontSize = Math.max(MIN_FONT_SIZE, currFontSize);
Font f = l.getFont();
Rectangle r1 = new Rectangle(getTextSize(l, l.getFont()));
while (!r.contains(r1)) {
fontSize --;
if (fontSize <= MIN_FONT_SIZE)
break;
r1 = new Rectangle(getTextSize(l, f.deriveFont(f.getStyle(), fontSize)));
}
Rectangle r2 = new Rectangle();
while (fontSize < MAX_FONT_SIZE) {
r2.setSize(getTextSize(l, f.deriveFont(f.getStyle(),fontSize+1)));
if (!r.contains(r2)) {
break;
}
fontSize++;
}
setFont(f.deriveFont(f.getStyle(),fontSize));
repaint();
}
private Dimension getTextSize(JLabel l, Font f) {
Dimension size = new Dimension();
//g.setFont(f); // superfluous.
FontMetrics fm = g.getFontMetrics(f);
size.width = fm.stringWidth(l.getText());
size.height = fm.getHeight();
return size;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.g=g;
}
public static void main(String[] args) throws Exception {
FontResizingLabel label=new FontResizingLabel("Some text");
JFrame frame=new JFrame("Resize label font");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(label);
frame.setSize(300,300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

set maximum size of JPanel inside BorderLayout.CENTER

I have a JPanel inside BorderLayout.CENTER
The JPanel has a Grid Layout, and I want it to expand with the CENTER for its width, but the height must stop at a maximum and use the preferredSize when possible.
I have this code
JPanel wrapperCenterPanel = new JPanel(new FlowLayout());
wrapperCenterPanel.add(centerPanel);
panel.add(wrapperCenterPanel, BorderLayout.CENTER);
centerPanel is my panel (uses GridLayout), I'm wrapping it with a FlowLayout panel, and putting this last one in the CENTER.
Now the size is the preferred one, but it's fixed!! The height doesn't shrink if necessary, and neither does the width.
How can I do this?
Try using a BoxLayout as the wrapper panel. A BoxLayout respects the maximum/minimum and preferred size of a component.
I think that not possible with BorderLayout, especially for BorderLayout.CENTER area, nor without side_effects as blinking UFO on the screen from the code
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ComponentEvent;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class CustomComponent extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent() {
setTitle("Custom Component Graphics2D");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
CustomComponents cc = new CustomComponents();
cc.addComponentListener(new java.awt.event.ComponentAdapter() {
#Override
public void componentResized(ComponentEvent event) {
setSize(Math.min(getPreferredSize().width, getWidth()),
Math.min(getPreferredSize().height, getHeight()));
}
});
add(cc, BorderLayout.CENTER);
CustomComponents cc1 = new CustomComponents();
add(cc1, BorderLayout.EAST);
pack();
// enforces the minimum size of both frame and component
setMinimumSize(getSize());
//setMaximumSize(getMaximumSize());
setVisible(true);
}
public static void main(String[] args) {
CustomComponent main = new CustomComponent();
main.display();
}
}
class CustomComponents extends JComponent {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(800, 600);
}
#Override
public void paintComponent(Graphics g) {
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
}
}
The FLowLayout layout manager does not redistribute free space; it uses each component's preferred size (see FlowLayout documentation in the Java API).
I personally would change your wrapper panel's layout manager to a GridBagLayout, and add your centerPanel into it, specify a proper GridBagConstraints object to handle the space distribution as you need.

Categories

Resources