测量多行文本的紧边界框
Measuring tight bounds box of multiline text
我正在使用 StaticLayout
在 canvas 上绘制多行文本,并且我想在绘制之前测量文本周围最紧密的边界框,(文本可能有不同的大小、字体、样式等...),我想要这样的东西:
Size measureText(String text, float size, Font font, etc...)
我希望它 return 文本周围最紧密的边界框,即(如果我们谈论的是文本的像素):
(leftest_pixel - rightest_pixel, highest_pixel - lowest_pixels)
如果文本是单行的,我可以这样做:
Paint paint = new Paint();
...
paint.getTextBounds(text, 0, size, rect);
但是由于文本可能有多行,我必须考虑行距和字形下降以及所有其他字体参数...因此下一个选项是使用 StaticLayout
和 maximalLineWidth
(为了打破行),但 StaticLayout
不计算最紧密的框,它会在顶部和底部添加一些填充(因为它基本上是行数乘以最大行高):
比如绿框是用StaticLayout
测量的结果,红框是我要收的框:
我该怎么做?
谢谢。
构建 StaticLayout,然后使用 TextPaint 中的方法确定每一行的边界。整个多行文本的边界是第一行的上边界、最后一行的下边界和所有行的最左边界和最右边界。
这是一个演示此概念的示例自定义视图。布局只是自定义视图,宽度为 200dp
,高度为 wrap_content
。
MyStaticText
public class MyStaticText extends View {
private final String mText = "This is some longer text to test multiline layout.";
private TextPaint mTextPaint;
private StaticLayout mStaticLayout;
private final Paint mRectPaint = new Paint();
public MyStaticText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(FONT_SIZE * getResources().getDisplayMetrics().density);
mTextPaint.setColor(Color.BLACK);
mRectPaint.setStyle(Paint.Style.STROKE);
mRectPaint.setStrokeWidth(2f);
mRectPaint.setColor(Color.RED);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthAskedFor = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mStaticLayout = new StaticLayout(mText, mTextPaint, widthAskedFor, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);
} else {
throw new RuntimeException("View width must be exactly specified.");
}
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height;
if (heightMode == MeasureSpec.AT_MOST) {
height = mStaticLayout.getHeight() + getPaddingTop() + getPaddingBottom();
} else {
throw new RuntimeException("View height must be 'wrap_content'.");
}
setMeasuredDimension(widthAskedFor, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mStaticLayout.draw(canvas);
// Start with bounds of first line.
Rect textBounds = getLineBounds(0);
// Check bounds of last line since it will be the bottom of the bounding rectangle.
Rect lineBounds = getLineBounds(mStaticLayout.getLineCount() - 1);
if (lineBounds.bottom > textBounds.bottom) {
textBounds.bottom = lineBounds.bottom;
}
// Now check all lines for min left bound and max right bound.
for (int line = 0; line < mStaticLayout.getLineCount(); line++) {
lineBounds = getLineBounds(line);
if (lineBounds.left < textBounds.left) {
textBounds.left = lineBounds.left;
}
if (lineBounds.right > textBounds.right) {
textBounds.right = lineBounds.right;
}
}
canvas.drawRect(textBounds, mRectPaint);
}
private Rect getLineBounds(int line) {
int firstCharOnLine = mStaticLayout.getLineStart(line);
int lastCharOnLine = mStaticLayout.getLineVisibleEnd(line);
String s = mText.substring(firstCharOnLine, lastCharOnLine);
// bounds will store the rectangle that will circumscribe the text.
Rect bounds = new Rect();
// Get the bounds for the text. Top and bottom are measured from the baseline. Left
// and right are measured from 0.
mTextPaint.getTextBounds(s, 0, s.length(), bounds);
int baseline = mStaticLayout.getLineBaseline(line);
bounds.top = baseline + bounds.top;
bounds.bottom = baseline + bounds.bottom;
return bounds;
}
private static final int FONT_SIZE = 48;
}
Here 是一个具有更通用解决方案的演示应用程序。
我正在使用 StaticLayout
在 canvas 上绘制多行文本,并且我想在绘制之前测量文本周围最紧密的边界框,(文本可能有不同的大小、字体、样式等...),我想要这样的东西:
Size measureText(String text, float size, Font font, etc...)
我希望它 return 文本周围最紧密的边界框,即(如果我们谈论的是文本的像素):
(leftest_pixel - rightest_pixel, highest_pixel - lowest_pixels)
如果文本是单行的,我可以这样做:
Paint paint = new Paint();
...
paint.getTextBounds(text, 0, size, rect);
但是由于文本可能有多行,我必须考虑行距和字形下降以及所有其他字体参数...因此下一个选项是使用 StaticLayout
和 maximalLineWidth
(为了打破行),但 StaticLayout
不计算最紧密的框,它会在顶部和底部添加一些填充(因为它基本上是行数乘以最大行高):
比如绿框是用StaticLayout
测量的结果,红框是我要收的框:
我该怎么做? 谢谢。
构建 StaticLayout,然后使用 TextPaint 中的方法确定每一行的边界。整个多行文本的边界是第一行的上边界、最后一行的下边界和所有行的最左边界和最右边界。
这是一个演示此概念的示例自定义视图。布局只是自定义视图,宽度为 200dp
,高度为 wrap_content
。
MyStaticText
public class MyStaticText extends View {
private final String mText = "This is some longer text to test multiline layout.";
private TextPaint mTextPaint;
private StaticLayout mStaticLayout;
private final Paint mRectPaint = new Paint();
public MyStaticText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(FONT_SIZE * getResources().getDisplayMetrics().density);
mTextPaint.setColor(Color.BLACK);
mRectPaint.setStyle(Paint.Style.STROKE);
mRectPaint.setStrokeWidth(2f);
mRectPaint.setColor(Color.RED);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthAskedFor = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mStaticLayout = new StaticLayout(mText, mTextPaint, widthAskedFor, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);
} else {
throw new RuntimeException("View width must be exactly specified.");
}
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height;
if (heightMode == MeasureSpec.AT_MOST) {
height = mStaticLayout.getHeight() + getPaddingTop() + getPaddingBottom();
} else {
throw new RuntimeException("View height must be 'wrap_content'.");
}
setMeasuredDimension(widthAskedFor, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mStaticLayout.draw(canvas);
// Start with bounds of first line.
Rect textBounds = getLineBounds(0);
// Check bounds of last line since it will be the bottom of the bounding rectangle.
Rect lineBounds = getLineBounds(mStaticLayout.getLineCount() - 1);
if (lineBounds.bottom > textBounds.bottom) {
textBounds.bottom = lineBounds.bottom;
}
// Now check all lines for min left bound and max right bound.
for (int line = 0; line < mStaticLayout.getLineCount(); line++) {
lineBounds = getLineBounds(line);
if (lineBounds.left < textBounds.left) {
textBounds.left = lineBounds.left;
}
if (lineBounds.right > textBounds.right) {
textBounds.right = lineBounds.right;
}
}
canvas.drawRect(textBounds, mRectPaint);
}
private Rect getLineBounds(int line) {
int firstCharOnLine = mStaticLayout.getLineStart(line);
int lastCharOnLine = mStaticLayout.getLineVisibleEnd(line);
String s = mText.substring(firstCharOnLine, lastCharOnLine);
// bounds will store the rectangle that will circumscribe the text.
Rect bounds = new Rect();
// Get the bounds for the text. Top and bottom are measured from the baseline. Left
// and right are measured from 0.
mTextPaint.getTextBounds(s, 0, s.length(), bounds);
int baseline = mStaticLayout.getLineBaseline(line);
bounds.top = baseline + bounds.top;
bounds.bottom = baseline + bounds.bottom;
return bounds;
}
private static final int FONT_SIZE = 48;
}
Here 是一个具有更通用解决方案的演示应用程序。