【问题标题】:Measuring text height to be drawn on Canvas ( Android )测量要在 Canvas 上绘制的文本高度(Android)
【发布时间】:2011-04-08 22:24:05
【问题描述】:

任何直接的方法来测量文本的高度? 我现在这样做的方式是使用 Paint 的 measureText() 来获取宽度,然后通过反复试验找到一个值来获取近似高度。我也一直在搞乱FontMetrics,但所有这些似乎都是很糟糕的近似方法。

我正在尝试针对不同的分辨率进行缩放。我可以做到,但我最终得到了令人难以置信的冗长代码,其中包含大量计算以确定相对大小。我讨厌它!必须有更好的方法。

【问题讨论】:

    标签: android graphics view android-canvas


    【解决方案1】:

    paint.getTextBounds()(对象方法)呢

    【讨论】:

    • 当我评估文本的高度时,这会产生非常奇怪的结果。短文本导致高度为 12,而真正长文本导致高度为 16(给定字体大小为 16)。对我来说没有意义(android 2.3.3)
    • 高度的变化是你在文本中有下降的地方,即“高”比“低”高,因为该行下方的 g 部分
    【解决方案2】:

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

    【讨论】:

    • 如果您要处理多个文本段,则不会。原因在我上面的回答中。
    【解决方案3】:

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

    这意味着,例如“Py”的高度将不同于“py”或“hi”或“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”),我们就可以在其余文本中重用它的对齐方式。这将使所有文本居中对齐以及基线对齐。

    【讨论】:

    • 很久没见过x >> 1了。仅为此投票:)
    • 一个好的现代编译器会看到x / 2并将其优化为x >> 1
    • @keaukraine x / 2 在阅读代码时更加友好,考虑到 Chris 的评论。
    • 在这个例子中buffer 是什么?是canvas 传递给draw(Canvas) 方法吗?
    • @AutonomousApps 是的,它是画布
    【解决方案4】:

    您可以使用 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();
    

    【讨论】:

    • 24.0f 来自哪里?
    • 24.0f 这只是文本大小的示例
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

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

      我有一个正方形的自定义视图(宽度 = 高度),我想为其分配一个字符。 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();
          }
      }
      

      【讨论】:

      • -1 用于 onDraw() 中的分配:如果您要在类中声明字段(在构造函数中启动)并在 onDraw() 中重新使用它们,它将产生大量的性能优势。
      【解决方案7】:

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

      找出高度的另一种方法是

      mPaint.getTextSize();
      

      【讨论】:

        【解决方案8】:

        根据您的需要,有不同的方法来测量高度。

        getTextBounds

        如果您正在执行诸如将少量固定文本精确居中的操作,您可能需要getTextBounds。你可以像这样得到边界矩形

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

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

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

        Paint.FontMetrics

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

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

        基线是文本所在的行。下降通常是角色将在该线之下的最远距离,而上升通常是角色将在该线之上的最远距离。要获得高度,您必须减去 ascent,因为它是负值。 (基线是y=0y 缩小屏幕。)

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

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

        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(); 
        

        【讨论】:

        • 很好的解释。截图来自哪个应用程序?
        • @MichealJohnson,我将应用添加为GitHub project here
        • 那么“getTextSize”会给你什么?
        • Paint 的getTextSize() 为您提供以像素为单位的字体大小(而不是sp 单位)。 @androiddeveloper
        • 以像素为单位的字体大小与测量高度和FontMetrics dimensions 有什么关系?这是我想进一步探讨的问题。
        猜你喜欢
        • 2015-06-20
        • 1970-01-01
        • 2012-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-26
        • 2021-03-14
        • 1970-01-01
        相关资源
        最近更新 更多