【问题标题】:Measuring tight bounds box of multiline text测量多行文本的紧密边界框
【发布时间】:2019-12-26 18:28:42
【问题描述】:

我正在使用StaticLayout 在画布上绘制多行文本,并且我想在绘制之前测量文本周围最紧密的边界框,(文本可以有不同的大小、字体、样式等... ),我想要这样的东西:

Size measureText(String text, float size, Font font, etc...)

我希望它返回文本周围最紧密的边界框,即(如果我们谈论的是文本的像素):

(leftest_pixel - rightest_pixel, highest_pixel - lowest_pixels)

如果文本是单行,我可以这样做:

Paint paint = new Paint();
...
paint.getTextBounds(text, 0, size, rect);

但由于文本可能有多行,我必须考虑行间距和字形下降以及所有其他字体参数......所以下一个选项是使用 StaticLayoutmaximalLineWidth(为了断线),但StaticLayout 不计算最紧凑的框,它会在顶部和底部添加一些填充(因为它基本上是行数乘以最大行高):

例如,绿色框是用StaticLayout测量的结果,红色框是我要接收的框:

我该怎么做? 谢谢。

【问题讨论】:

  • 不是答案,但您是否尝试过这里的其他 2 种方法:stackoverflow.com/a/42091739/608312
  • @JakeSteam 是的,其他方法在这里没有帮助,因为我必须使用静态布局才能打破线条

标签: android android-canvas android-graphics staticlayout


【解决方案1】:

构建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 是一个演示应用,具有更通用的解决方案。

【讨论】:

    猜你喜欢
    • 2023-02-25
    • 1970-01-01
    • 2012-08-23
    • 2010-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多