测量在Canvas(Android)上绘制的文本高度

时间:2010-09-06 21:12:11

标签: android graphics view android-canvas

任何直接测量文字高度的方法? 我现在这样做的方法是使用Paint的measureText()来获取宽度,然后通过试验和错误找到一个值来获得近似高度。我也一直在讨论FontMetrics,但所有这些似乎都是近似的方法。

我正在努力扩展不同分辨率的东西。我可以做到,但我最终得到了令人难以置信的冗长代码,并通过大量计算来确定相对大小。我讨厌它!必须有更好的方法。

8 个答案:

答案 0 :(得分:135)

根据您的需要,有多种方法可以测量身高。

getTextBounds

如果您正在做一些精确定位少量固定文本的内容,您可能需要getTextBounds。您可以像这样获得边界矩形

Rect bounds = new Rect();
mTextPaint.getTextBounds(mText, 0, mText.length(), bounds);
int height = bounds.height();

如下图所示,不同的字符串会有不同的高度(以红色显示)。

enter image description here

在某些情况下,无论文本是什么,只需要一个恒定的高度,这些不同的高度都可能是一个缺点。请参阅下一节。

Paint.FontMetrics

您可以根据字体指标计算字体的高度。高度始终相同,因为它是从字体中获取的,而不是任何特定的文本字符串。

Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float height = fm.descent - fm.ascent;

基线是文本所在的行。下降通常是最远的一个角色将在线下方,并且上升通常是最远的一个角色将在线上方。要获得高度,您必须减去上升,因为它是负值。 (基线为y=0y会减少屏幕。)

请看下面的图片。两个字符串的高度均为234.375

enter image description here

如果您想要行高而不仅仅是文本高度,您可以执行以下操作:

float height = fm.bottom - fm.top + fm.leading; // 265.4297

这些是该行的bottomtop。前导(行间距)通常为零,但无论如何都应该添加它。

上面的图片来自this project。您可以使用它来查看字体度量标准的工作方式。

StaticLayout

要测量多行文字的高度,您应该使用StaticLayout。我在this answer中详细讨论了它,但获得这个高度的基本方法是这样的:

String text = "This is some text. This is some text. This is some text. This is some text. This is some text. This is some text.";

TextPaint myTextPaint = new TextPaint();
myTextPaint.setAntiAlias(true);
myTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
myTextPaint.setColor(0xFF000000);

int width = 200;
Layout.Alignment alignment = Layout.Alignment.ALIGN_NORMAL;
float spacingMultiplier = 1;
float spacingAddition = 0;
boolean includePadding = false;

StaticLayout myStaticLayout = new StaticLayout(text, myTextPaint, width, alignment, spacingMultiplier, spacingAddition, includePadding);

float height = myStaticLayout.getHeight(); 

答案 1 :(得分:124)

paint.getTextBounds()(对象方法)

怎么样?

答案 2 :(得分:81)

@ bramp的回答是正确的 - 部分原因在于它没有提到计算出的边界将是包含完全隐含起始坐标0,0的文本的最小矩形。

这意味着,例如" Py"的高度。将与" py"的高度不同或者"嗨"或" oi"或者" aw"因为像素方面它们需要不同的高度。

这绝不等同于经典java中的FontMetrics。

虽然文字的宽度不是很大,但是高度是。

特别是,如果您需要垂直居中对齐绘制的文本,请尝试获取文本的边界" a" (不带引号),而不是使用您打算绘制的文本。 对我有用......

这就是我的意思:

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);

paint.setStyle(Paint.Style.FILL);
paint.setColor(color);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(textSize);

Rect bounds = new Rect();
paint.getTextBounds("a", 0, 1, bounds);

buffer.drawText(this.myText, canvasWidth >> 1, (canvasHeight + bounds.height()) >> 1, paint);
// remember x >> 1 is equivalent to x / 2, but works much much faster

垂直居中对齐文本意味着垂直居中对齐边界矩形 - 这对于不同的文本(大写字母,长字母等)是不同的。但我们实际想要做的是同时对齐渲染文本的基线,使它们看起来没有凸起或凹凸不平。因此,只要我们知道最小字母的中心(" a"例如),我们就可以将其对齐重用于其余的文本。这将使所有文本居中对齐以及基线对齐它们。

答案 3 :(得分:14)

高度是您在Paint变量上设置的文本大小。

找出高度的另一种方法是

mPaint.getTextSize();

答案 4 :(得分:3)

您可以使用android.text.StaticLayout类指定所需的边界,然后调用getHeight()。您可以通过调用其draw(Canvas)方法来绘制文本(包含在布局中)。

答案 5 :(得分:2)

您可以使用getTextSize()方法获取Paint对象的文本大小。 例如:

Paint mTextPaint = new Paint (Paint.ANTI_ALIAS_FLAG);
//use densityMultiplier to take into account different pixel densities
final float densityMultiplier = getContext().getResources()
            .getDisplayMetrics().density;  
mTextPaint.setTextSize(24.0f*densityMultiplier);

//...

float size = mTextPaint.getTextSize();

答案 6 :(得分:1)

您必须使用从Rect.width()返回的Rect.Height()getTextBounds()。这对我有用。

答案 7 :(得分:-1)

如果还有人有问题,这是我的代码。

我有一个自定义视图,它是正方形(宽度=高度),我想给它分配一个字符。 onDraw()显示了如何获得角色的高度,虽然我没有使用它。角色将显示在视图中间。

public class SideBarPointer extends View {

    private static final String TAG = "SideBarPointer";

    private Context context;
    private String label = "";
    private int width;
    private int height;

    public SideBarPointer(Context context) {
        super(context);
        this.context = context;
        init();
    }

    public SideBarPointer(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        init();
    }

    public SideBarPointer(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
        init();
    }

    private void init() {
//        setBackgroundColor(0x64FF0000);
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        height = this.getMeasuredHeight();
        width = this.getMeasuredWidth();

        setMeasuredDimension(width, width);
    }

    protected void onDraw(Canvas canvas) {
        float mDensity = context.getResources().getDisplayMetrics().density;
        float mScaledDensity = context.getResources().getDisplayMetrics().scaledDensity;

        Paint previewPaint = new Paint();
        previewPaint.setColor(0x0C2727);
        previewPaint.setAlpha(200);
        previewPaint.setAntiAlias(true);

        Paint previewTextPaint = new Paint();
        previewTextPaint.setColor(Color.WHITE);
        previewTextPaint.setAntiAlias(true);
        previewTextPaint.setTextSize(90 * mScaledDensity);
        previewTextPaint.setShadowLayer(5, 1, 2, Color.argb(255, 87, 87, 87));

        float previewTextWidth = previewTextPaint.measureText(label);
//        float previewTextHeight = previewTextPaint.descent() - previewTextPaint.ascent();
        RectF previewRect = new RectF(0, 0, width, width);

        canvas.drawRoundRect(previewRect, 5 * mDensity, 5 * mDensity, previewPaint);
        canvas.drawText(label, (width - previewTextWidth)/2, previewRect.top - previewTextPaint.ascent(), previewTextPaint);

        super.onDraw(canvas);
    }

    public void setLabel(String label) {
        this.label = label;
        Log.e(TAG, "Label: " + label);

        this.invalidate();
    }
}