将文本和图形集成

Download Sample下载示例

了解如何确定呈现的文本字符串的大小,以便将文本与 SkiaSharp 图形集成

本文演示如何测量文本、将文本缩放为特定大小并将文本与其他图形集成:

Text surrounded by rectangles

该图像还包括一个圆角矩形。 SkiaSharp Canvas 类包括用于绘制矩形的 DrawRect 方法,以及用于绘制带圆角的矩形的 DrawRoundRect 方法。 这些方法允许将矩形定义为 SKRect 值或以其他方式定义矩形。

“带框文本”页面会将一个短文本字符串在页面上居中显示,并使用一个由一对圆角矩形组成的框架将其包围。 FramedTextPage 类显示了这是如何实现的。

在 SkiaSharp 中,可以使用 SKPaint 类设置文本和字体属性,但也可以使用该类来获取呈现的文本大小。 以下 PaintSurface 事件处理程序的开头调用了两个不同的 MeasureText 方法。 第一个 MeasureText 调用具有简单的 string 参数,并将根据当前字体属性返回文本的像素宽度。 然后,程序将根据呈现的宽度、当前 TextSize 属性和显示区域的宽度来计算 SKPaint 对象的新 TextSize 属性。 此计算旨在设置 TextSize,以便以屏幕宽度的 90% 呈现文本字符串:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    string str = "Hello SkiaSharp!";

    // Create an SKPaint object to display the text
    SKPaint textPaint = new SKPaint
    {
        Color = SKColors.Chocolate
    };

    // Adjust TextSize property so text is 90% of screen width
    float textWidth = textPaint.MeasureText(str);
    textPaint.TextSize = 0.9f * info.Width * textPaint.TextSize / textWidth;

    // Find the text bounds
    SKRect textBounds = new SKRect();
    textPaint.MeasureText(str, ref textBounds);
    ...
}

第二个 MeasureText 调用具有 SKRect 参数,因此它将同时获取所呈现文本的宽度和高度。 此 SKRect 值的 Height 属性取决于文本字符串中是否存在大写字母、上标和下标。 例如,对于文本字符串“mom”、“cat”和“dog”将会报告不同的 Height 值。

如果文本通过 X 和 Y 位置为 0 的 DrawText 调用显示,SKRect 结构的 LeftTop 属性将指示所呈现文本左上角的坐标。 例如,当此程序在 iPhone 7 模拟器上运行时,将为 TextSize 分配值 90.6254,这是在首次调用 MeasureText 后的计算结果。 从对 MeasureText 的第二次调用中获取的 SKRect 值将具有以下属性值:

  • Left = 6
  • Top = –68
  • Width = 664.8214
  • Height = 88

请记住,传递给 DrawText 方法的 X 和 Y 坐标指定的是基线处文本的左侧。 Top 值表示文本在该基线之上延伸了 68 个像素,并且在基线之下延伸了 20 个像素(88 减去 68)。 Left 值 6 表示文本从 DrawText 调用中的 X 值右侧六个像素开始。 这允许正常的字符间间距。 如果要在显示器的左上角紧密地显示文本,请将这些 LeftTop 值的负值作为 DrawText 的 X 和 Y 坐标传递,在本示例中为 –6 和 68。

SKRect 结构定义了多个方便的属性和方法,其中一些属性和方法将用于 PaintSurface 处理程序的其余部分。 MidXMidY 值表示矩形中心的坐标。 (在 iPhone 7 示例中,这些值为 338.4107 和 –24。)以下代码使用这些值来通过最简单的方式计算将文本居中显示在显示器上的坐标:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    ...
    // Calculate offsets to center the text on the screen
    float xText = info.Width / 2 - textBounds.MidX;
    float yText = info.Height / 2 - textBounds.MidY;

    // And draw the text
    canvas.DrawText(str, xText, yText, textPaint);
    ...
}

SKImageInfo 信息结构还定义了 SKRect 类型的 Rect 属性,因此还可以按如下所示的方式计算 xTextyText

float xText = info.Rect.MidX - textBounds.MidX;
float yText = info.Rect.MidY - textBounds.MidY;

PaintSurface 处理程序以两个 DrawRoundRect 调用结束,这两个调用都需要 SKRect 参数。 此 SKRect 值基于从 MeasureText 方法获取的 SKRect 值,但不能与之相同。 首先,它需要稍大一点,这样圆角矩形就不会在文本的边缘上绘制。 其次,它需要在空间中移动,以便 LeftTop 值对应于要放置矩形的位置的左上角。 这两个作业由 SKRect 定义的 OffsetInflate 方法完成:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    ...
    // Create a new SKRect object for the frame around the text
    SKRect frameRect = textBounds;
    frameRect.Offset(xText, yText);
    frameRect.Inflate(10, 10);

    // Create an SKPaint object to display the frame
    SKPaint framePaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        StrokeWidth = 5,
        Color = SKColors.Blue
    };

    // Draw one frame
    canvas.DrawRoundRect(frameRect, 20, 20, framePaint);

    // Inflate the frameRect and draw another
    frameRect.Inflate(10, 10);
    framePaint.Color = SKColors.DarkBlue;
    canvas.DrawRoundRect(frameRect, 30, 30, framePaint);
}

接下来,该方法的其余部分是直截了当的。 它为边框创建了另一个 SKPaint 对象,并调用 DrawRoundRect 两次。 第二次调用使用了一个再扩大 10 个像素的矩形。 第一次调用指定了 20 个像素的圆角半径。 第二次指定的圆角半径为 30 个像素,因此两个矩形看起来是平行的:

Triple screenshot of the Framed Text page

你可以侧转手机或模拟器,以查看文本和框架的大小增加情况。

如果只需要在屏幕上居中显示一些文本,则可以约莫着执行此操作,而无需测量文本。 相对的,请将 SKPaintTextAlign 属性设置为枚举成员 SKTextAlign.Center。 然后,你在 DrawText 方法中指定的 X 坐标将指示文本的水平中心的位置。 如果将屏幕的中点传递到 DrawText 方法,文本将水平居中,并且几乎垂直居中,因为基线将垂直居中

文本可以像任何其他图形对象一样进行处理。 一个简单的选项是显示文本字符的轮廓:

Triple screen shot of the Outlined Text page

只需将 SKPaint 对象的普通 Style 属性从默认设置 SKPaintStyle.Fill 更改为 SKPaintStyle.Stroke,并指定笔划宽度即可实现此目的。 “轮廓文本”页面的 PaintSurface 处理程序显示了这是如何实现的:

void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
    SKImageInfo info = args.Info;
    SKSurface surface = args.Surface;
    SKCanvas canvas = surface.Canvas;

    canvas.Clear();

    string text = "OUTLINE";

    // Create an SKPaint object to display the text
    SKPaint textPaint = new SKPaint
    {
        Style = SKPaintStyle.Stroke,
        StrokeWidth = 1,
        FakeBoldText = true,
        Color = SKColors.Blue
    };

    // Adjust TextSize property so text is 95% of screen width
    float textWidth = textPaint.MeasureText(text);
    textPaint.TextSize = 0.95f * info.Width * textPaint.TextSize / textWidth;

    // Find the text bounds
    SKRect textBounds = new SKRect();
    textPaint.MeasureText(text, ref textBounds);

    // Calculate offsets to center the text on the screen
    float xText = info.Width / 2 - textBounds.MidX;
    float yText = info.Height / 2 - textBounds.MidY;

    // And draw the text
    canvas.DrawText(text, xText, yText, textPaint);
}

另一个常见的图形对象是位图。 这个庞大的主题将在 SkiaSharp 位图部分进行深入介绍,但下一篇文章 SkiaSharp 中的位图基础知识对其作了简单介绍。