Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 789
fusio Avatar asked Mar 21 '12 23:03

fusio


People also ask

How do you change the size of a label in Java?

getSize() * widthRatio); int componentHeight = label. getHeight(); // Pick a new font size so it will not be larger than the height of label. int fontSizeToUse = Math. min(newFontSize, componentHeight); // Set the label's font size to the newly determined size.

How do I change the font size in a JPanel?

You can call the method setFont() from you JPanel and give it a Font as parameter. Example : setFont(new java. awt.


3 Answers

You can use any of the following methods:

  • by @trashgod

  • by @StanislavL

  • by @coobird

like image 113
mKorbel Avatar answered Oct 08 '22 10:10

mKorbel


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.

like image 29
fusio Avatar answered Oct 08 '22 10:10

fusio


If performance speed is an issue, then you might find the following information about the 3 methods pointed to by MKorbel above useful.

  1. Coobird's code has some limitations if used on a multi-call basis (eg in a sizeChanged Listener or a LayoutManager)

  2. 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.)

  3. 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);
    }
}
like image 38
Warren K Avatar answered Oct 08 '22 09:10

Warren K