I have a JTextField which may vary in size depending on resolution. The JTextField will hold a 3 letter string.
I want to set the font size of the JTextField in such a way to MAXIMIZE the font size while still having the text FIT inside of the JTextField perfectly.
Is there an algorithm to do this?
Given the width of the String in fontsize a
is x
. The available space is s
. Then you can obviously scale up your font with factor: s / x
. So choose a * s / x
as fontsize. To know x
, calculate the width of the string with an arbitrary fontsize a
.
Yes, I know this is an ancient thread... but I was just having a similar problem and created custom JTextField class to solve it. This class uses some "calibration text" which is unique to my implementation, but that'd be easy to change for other uses.
So, it will change the font in two scenarios:
public class DynamicText extends JTextField implements ComponentListener
{
private final int MIN_SIZE = 5;
private final int MAX_SIZE = 100;
private int pfvMinFontSize = 5; // minimum font size
private int pfvMaxFontSize = 100; // maximum font size
private boolean pfvAutoScale = true; // enabled the autoscaling feature
int cached_width = 0;
int cached_height = 0;
int widthLimitPixels;
float calcMax;
public DynamicText()
{
super();
this.getDocument().addDocumentListener(new TextChanged(this));
addComponentListener(this);
}
public DynamicText(int cols)
{
super(cols);
this.getDocument().addDocumentListener(new TextChanged(this));
addComponentListener(this);
}
public int getMinFontSize()
{
return pfvMinFontSize;
}
public void setMinFontSize(int val)
{
if (val >= MIN_SIZE)
{
pfvMinFontSize = val;
}
}
public int getMaxFontSize()
{
return pfvMaxFontSize;
}
// TODO: Check to make sure max is larger than min
public void setMaxFontSize(int val)
{
if (val <= MAX_SIZE)
{
pfvMaxFontSize = val;
}
}
public boolean getAutoScale()
{
return pfvAutoScale;
}
public void setAutoScale(boolean val)
{
pfvAutoScale = val;
}
@Override
public void componentResized(ComponentEvent e)
{
// to keep from having to process multiple events
if (this.getWidth() != cached_width || this.getHeight() != cached_height)
{
cached_width = this.getWidth();
cached_height = this.getHeight();
/*
* I do not consider this part of the optional AutoScale feature
* since this will just scale the font for the "calibrated"
* text this the maximum size
*/
Font ft = this.getFont();
float fontSize = pfvMinFontSize;
FontMetrics metrics;
int widthPixels = 0;
int widthLimitPixels = this.getWidth() - this.getMargin().left - this.getMargin().right;
float calcMax = (this.getHeight() - this.getMargin().top - this.getMargin().bottom) / 1.5f;
while (widthPixels < widthLimitPixels && fontSize <= pfvMaxFontSize && fontSize <= calcMax)
{
++fontSize;
Font temp = ft.deriveFont(fontSize);
metrics = this.getFontMetrics(temp);
widthPixels = metrics.stringWidth("00000000 00000000 00000000 00000000 .b.");
}
--fontSize;
this.setFont(ft.deriveFont(fontSize));
}
}
@Override
public void componentMoved(ComponentEvent e)
{
// nothing
}
@Override
public void componentShown(ComponentEvent e)
{
// nothing
}
@Override
public void componentHidden(ComponentEvent e)
{
// nothing
}
private class TextChanged implements DocumentListener
{
JTextField jtf;
public TextChanged(JTextField source)
{
jtf = source;
}
@Override
public void insertUpdate(DocumentEvent e)
{
resizeFont();
}
@Override
public void removeUpdate(DocumentEvent e)
{
resizeFont();
}
@Override
public void changedUpdate(DocumentEvent e)
{
// not applicable here...
}
private void resizeFont()
{
if (pfvAutoScale && jtf.getHeight() > 0)
{
Font ft = jtf.getFont();
String viewText = jtf.getText();
float fontSize = pfvMinFontSize;
FontMetrics metrics;
int widthPixels = 0;
int widthLimitPixels = jtf.getWidth() - jtf.getMargin().left - jtf.getMargin().right;
float calcMax = (jtf.getHeight() - jtf.getMargin().top - jtf.getMargin().bottom) / 1.5f;
while (widthPixels < widthLimitPixels && fontSize <= pfvMaxFontSize && fontSize <= calcMax)
{
++fontSize;
Font temp = ft.deriveFont(fontSize);
metrics = jtf.getFontMetrics(temp);
widthPixels = metrics.stringWidth(viewText);
}
--fontSize;
jtf.setFont(ft.deriveFont(fontSize));
}
}
}
}
I have used Martijn's answer, along with the following answers:
String length in pixels in Java
Java: Getting a font with a specific height in pixels
...in order to write a full answer to my question. Here it goes. Thanks to all who contributed.
You will need to import the following:
import javax.swing.JTextField;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.awt.FontMetrics;
import java.lang.Math;
.................................
public int getFontSize(JTextField text, int columnsToHold){
//Create a sample test String (we will it later in our calculations)
String testString = "";
for(int i = 0; i<columnsToHold; i++){
testString = testString + "5";
}
//size will hold the optimal Vertical point size for the font
Boolean up = null;
int size = text.getHeight();
Font font;
while (true) {
font = new Font("Default", 0, size);
int testHeight = getFontMetrics(font).getHeight();
if (testHeight < height && up != Boolean.FALSE) {
size++;
up = Boolean.TRUE;
} else if (testHeight > height && up != Boolean.TRUE) {
size--;
up = Boolean.FALSE;
} else {
break;
}
}
//At this point, size holds the optimal Vertical font size
//Now we will calculate the width of the sample string
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
FontMetrics fm = img.getGraphics().getFontMetrics(font);
int width = fm.stringWidth(testString);
//Using Martijn's answer, we calculate the optimal Horizontal font size
int newFontSize = size * textos[0].getWidth()/width;
//The perfect font size will be the minimum between both optimal font sizes.
//I have subtracted 2 from each font so that it is not too tight to the edges
return Math.min(newFontSize-2, size-2);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.