質問
私は、文字列の高さを取得するためにFontMetrics.getHeight()
を使用していますが、それは私に間違った値を与える、文字列の文字のディセンダーを切断します。私が使用することができ、より良い機能がありますか?
解決
以下getStringBounds()
方法は、テキストの1行の文字列のために非常にうまく機能現在GlyphVector
フォントのためGraphics2D
、に基づいています
public class StringBoundsPanel extends JPanel
{
public StringBoundsPanel()
{
setBackground(Color.white);
setPreferredSize(new Dimension(400, 247));
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// must be called before getStringBounds()
g2.setFont(getDesiredFont());
String str = "My Text";
float x = 140, y = 128;
Rectangle bounds = getStringBounds(g2, str, x, y);
g2.setColor(Color.red);
g2.drawString(str, x, y);
g2.setColor(Color.blue);
g2.draw(bounds);
g2.dispose();
}
private Rectangle getStringBounds(Graphics2D g2, String str,
float x, float y)
{
FontRenderContext frc = g2.getFontRenderContext();
GlyphVector gv = g2.getFont().createGlyphVector(frc, str);
return gv.getPixelBounds(null, x, y);
}
private Font getDesiredFont()
{
return new Font(Font.SANS_SERIF, Font.BOLD, 28);
}
private void startUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) throws Exception
{
final StringBoundsPanel tb = new StringBoundsPanel();
SwingUtilities.invokeAndWait(new Runnable()
{
public void run()
{
tb.startUI();
}
});
}
}
私はわかりやすくするために輸入を省略したことに注意してください。
結果:
他のヒント
あなたはそれが間違った値を返すどう思いますか?それが返すもののあなたの期待が仕様と一致しないことがはるかに可能性です。 Fontのいくつかのグリフがオーバーまたはそれらの値の下に行けば、それは完全に罰金だということに注意してください。
getMaxDescent()
とgetMaxAscent()
は、フォントのどのグリフのためにあなたにそれらのフィールドの絶対最大値を教えてくださいます。
あなたが特定の文字列のメトリックを知りたい場合は、その後、あなたは間違いなくgetLineMetrics()
を呼び出したい。
FontMetrics.getAscent()>と FontMetrics.getDescent() のは、トリックを行う可能性があります。
私は最近、私はフォントの特定の範囲に対する画素完璧な高さの測定値を必要に応じて下記のコードを書いた。
あなたは高速なコードが必要な場合は、(私はループのために持っている)私は、配列内の(1から100までなど)すべての値を取得するには、起動時に一度、それを実行することはお勧めして、代わりに配列を使用することになります。
コードは、基本的には250×250ビットマップに重ね(増加または必要に応じて低減する)同じ場所での入力文字列からすべての文字を描画し、それは次に下から、それが見つかった最大高さを返し、上部からのピクセルを探し始めます。それはそれは文字範囲のために設計された場合でも、通常の文字列を使用しています。これは、文字の繰り返しの一部として定期的に文字列を評価し、冗長性の一種があることを意味します。 「ABCD ... Z」して使用することができる他の文字:あなたの入力プログラムの文字列がアルファベット数(26)を超えるのであれば、「TRANGE」入力プログラムとして使用します。それは高速です。
希望に役立ちます。
public int getFontPixelHeight(float inSize, Paint sourcePaint, String tRange)
{
// It is assumed that the font is already set in the sourcePaint
int bW = 250, bH = 250; // bitmap's width and height
int firstContact = -1, lastContact = -2; // Used when scanning the pixel rows. Initial values are set so that if no pixels found, the returned result is zero.
int tX = (int)(bW - inSize)/2, tY = (int)(bH - inSize)/2; // Used for a rough centering of the displayed characters
int tSum = 0;
// Preserve the original paint attributes
float oldSize = sourcePaint.getTextSize();
int oldColor = sourcePaint.getColor();
// Set the size/color
sourcePaint.setTextSize(inSize); sourcePaint.setColor(Color.WHITE);
// Create the temporary bitmap/canvas
Bitmap.Config bConf = Bitmap.Config.ARGB_8888;
Bitmap hld = Bitmap.createBitmap(250, 250, bConf);
Canvas canv = new Canvas(hld);
for (int i = 0; i < bH; i++)
{
for (int j = 0; j < bW; j++)
{
hld.setPixel(j, i, 0); // Zero all pixel values. This might seem redundant, but I am not quite sure that creating a blank bitmap means the pixel color value is indeed zero, and I need them to be zero so the addition performed below is correct.
}
}
// Display all characters overlapping at the same position
for (int i = 0; i < tRange.length(); i++)
{
canv.drawText("" + tRange.charAt(i), tX, tY, sourcePaint);
}
for (int i = 0; i < bH; i++)
{
for (int j = 0; j < bW; j++)
{
tSum = tSum + hld.getPixel(j, i);
}
if (tSum > 0) // If we found at least a pixel, save row index and exit loop
{
firstContact = i;
tSum = 0; // Reset
break;
}
}
for (int i = bH - 1; i > 0 ; i--)
{
for (int j = 0; j < bW; j++)
{
tSum = tSum + hld.getPixel(j, i);
}
if (tSum > 0) // If we found at least a pixel, save row index and exit loop
{
lastContact = i;
break;
}
}
// Restore the initial attributes, just in case the paint was passed byRef somehow
sourcePaint.setTextSize(oldSize);
sourcePaint.setColor(oldColor);
return lastContact - firstContact + 1;
}
getHeight()
だけでそれを行うことができ、文字列の描画、文字列のディセンダーを遮断することはできません。あなたは何とか文字列を描画するgetHeight
から返された高さを使用していて、おそらくあなたは、高さを悪用しています。たとえば、あなたがのgetHeight(あるボックスの下部にある文字列の開始点を配置する場合)高い、その後、あなたのテキストのベースラインは、ボックスの下部端に座って、そして非常に高いディセンダーがクリップされます。
テキストジオメトリは奇妙な歴史的遺物を注入された複雑なトピックです。他の人が提案してきたように、あなたの箱の中に適切にベースラインを配置しようとするgetAscent
とgetDescent
を使用します。