2

I want to set the font size of button on componentResized event. I get the screensize and the size of the button. But can't get a way to calculate the preferred font size to set. If the Width of button is increased/decreased the font size also should increase/decrease accordingly. I can't get an object of Graphics also.

What could be a solution to work out for this problem ?

Tvd
  • 4,463
  • 18
  • 79
  • 125
  • duplicate? http://stackoverflow.com/questions/8181001/change-font-based-on-screen-size-change – kleopatra Nov 18 '11 at 14:32
  • See also [`JDigit`](http://stackoverflow.com/questions/4148336/jformattedtextfield-is-not-properly-cleared/4151403#4151403). – trashgod Nov 18 '11 at 15:57

4 Answers4

2

This is more of brute force solution.

1) You can try getting the font metrics using doing something like this:

// get metrics from the graphics
FontMetrics metrics = graphics.getFontMetrics(font);
// get the height of a line of text in this font and render context
int hgt = metrics.getHeight();
// get the advance of my text in this font and render context
int adv = metrics.stringWidth(text);
// calculate the size of a box to hold the text with some padding.
Dimension size = new Dimension(adv+2, hgt+2);

2) Then you can search through the fonts sizes that fit your component.

        Font savedFont = oldFont;
    for (int i = 0; i < 100; i++) {
        Font newFont = new Font(oldFont.getFontName(), oldFont.getStyle(), i);
        Dimension d = getFontSize(g,newFont,text);
        if(componentSize.height < d.height || componentSize.width < d.width){
            return savedFont;
        }
        savedFont = newFont;
    }

Putting it all together (note this is not tested)

public Dimension getFontSize(Graphics graphics, Font font, String text){
    // get metrics from the graphics
    FontMetrics metrics = graphics.getFontMetrics(font);
    // get the height of a line of text in this font and render context
    int hgt = metrics.getHeight();
    // get the advance of my text in this font and render context
    int adv = metrics.stringWidth(text);
    // calculate the size of a box to hold the text with some padding.
    Dimension size = new Dimension(adv+2, hgt+2);
    return size;
}

public Font findFont(Dimension componentSize, Font oldFont, String text, Graphics g){
    //search up to 100
    Font savedFont = oldFont;
    for (int i = 0; i < 100; i++) {
        Font newFont = new Font(oldFont.getFontName(), oldFont.getStyle(), i);
        Dimension d = getFontSize(g,newFont,text);
        if(componentSize.height < d.height || componentSize.width < d.width){
            return savedFont;
        }
        savedFont = newFont;
    }
    return oldFont;
}

EDIT using component to get FontMetrics

    public Dimension getFontSize(FontMetrics metrics ,Font font, String text){
    // get the height of a line of text in this font and render context
    int hgt = metrics.getHeight();
    // get the advance of my text in this font and render context
    int adv = metrics.stringWidth(text);
    // calculate the size of a box to hold the text with some padding.
    Dimension size = new Dimension(adv+2, hgt+2);
    return size;
}

public Font findFont(Component component, Dimension componentSize, Font oldFont, String text){
    //search up to 100
    Font savedFont = oldFont;
    for (int i = 0; i < 100; i++) {
        Font newFont = new Font(oldFont.getFontName(), oldFont.getStyle(), i);
        Dimension d = getFontSize(component.getFontMetrics(newFont),newFont,text);
        if(componentSize.height < d.height || componentSize.width < d.width){
            return savedFont;
        }
        savedFont = newFont;
    }
    return oldFont;
}

Measuring Text

Dimitry
  • 4,503
  • 6
  • 26
  • 40
  • how do I get Graphics - not using paint(Graphics g). Just using normal button and trying to work out. What to do to get graphics or so. – Tvd Nov 18 '11 at 15:16
  • Graphics is used to get FontMetrics. You can also use this: component.getFontMetrics(font); – Dimitry Nov 18 '11 at 15:28
1

Setting the text as in the last but one answer causes a high load on the CPU. My proposal is to scale the font accordingly:

public class ScalableJButton extends JButton {
    int mCurrentSize = 0;
    Font mInitialFont = null;
    int mInitialHeight;

    private static final long serialVersionUID = 1L;

    public ScalableJButton(String pString) {
        super(pString);
        init();
    }

    public ScalableJButton() {
        super();
        init();
    }

    private void init() {
        mInitialFont = getFont();
    }

    @Override
    protected void paintComponent(Graphics g) {
        if (mInitialHeight == 0) {
            mInitialHeight = getHeight();
        }
        int resizal = this.getHeight() * mInitialFont.getSize() / mInitialHeight;
        if(resizal != mCurrentSize){
            setFont(mInitialFont.deriveFont((float) resizal));
            mCurrentSize = resizal;
        }
        super.paintComponent(g);
    }
}
  • if it takes a high CPU usage. Why dont we use it 1st run when the splash screen comes in? And set the size over the configuration file or something. Then the main app use that size accordingly. Am I missing something? @chris – gumuruh Jan 20 '23 at 16:22
1

You could create a class ResizingButton with a paintComponent which wraps the super.paintComponent. Then do on the (Graphics2D)graphics a resizing transformation. The reason: font resizing is stepwise.

If you delve deeper into resizing fonts, use a FontRenderingContext with fractional metrics.


In response to the comment some code:

public class ResizingButton extends JButton {

@Override
protected void paintComponent(Graphics g) {        
    int h = this.getHeight();
    final int DEFAULT_H = 26;
    double resizal = ((double)h) / DEFAULT_H;

    String t = getText();
    setText("<html><span style='font-size:" + (resizal*11) + "'>" + t);
    super.paintComponent(g);
    setText(t);
}

I found that the above (Java 6/7) works. Graphics2D.scale also scales border and is hence too cumbersome.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • can you give me a small snipet to get the idea. ResizingButton will that be a component - subclass of Button ??? – Tvd Nov 18 '11 at 15:17
1

If you would like the font size to adjust automagically on resizing, you could try something like this:

import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;


public final class ButtonFrame extends JFrame {

ButtonFrame(String buttonText) {
    final JButton button = new JButton(buttonText);

    addComponentListener(new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
            float fittedFontSize = 1.0f;        
            while (getFittedText(button, fittedFontSize += 1.0f).equals(button.getText()));
            button.setFont(button.getFont().deriveFont(fittedFontSize - 1.0f));
            button.revalidate();
            button.repaint();
        }
    });

    getContentPane().add(button);
}

private String getFittedText(JButton button, float fontSize) {
    Insets i = button.getInsets();
    Rectangle viewRect = new Rectangle();
    Rectangle textRect = new Rectangle();
    Rectangle iconRect = new Rectangle();
    viewRect.x = i.left;
    viewRect.y = i.top;
    viewRect.width = button.getWidth() - (i.right + viewRect.x);
    viewRect.height = button.getHeight() - (i.bottom + viewRect.y);
    textRect.x = textRect.y = textRect.width = textRect.height = 0;
    iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;

    return SwingUtilities.layoutCompoundLabel(
            button, 
            button.getFontMetrics(button.getFont().deriveFont(fontSize)),
            button.getText(),
            button.getIcon(),
            button.getVerticalAlignment(),
            button.getHorizontalAlignment(),
            button.getVerticalTextPosition(),
            button.getHorizontalTextPosition(),
            viewRect,
            textRect,
            iconRect,
            button.getIconTextGap());
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {         
        @Override
        public void run() {
            JFrame frame = new ButtonFrame("sample text");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.setVisible(true);
            frame.pack();
        }
    });     
}
}
jelfsc
  • 36
  • 3
  • For a more responsive version, you could override JButton.paintComponent() instead of using the component listener. – jelfsc Nov 18 '11 at 15:45