如何根据view.Width动态自动设置FontSize?

How to dynamically and automatically set FontSize based on view.Width?

"TRYING OUT AUTO SIZING" 字体太大,以至于字符串的一部分被截掉(当文本太大时,一部分被截掉并用省略号代替)。

"test text" 字体太小。 fontSize 是其各自视图的三分之一(估计)。

如何将元素的字体大小设置为元素(和/或包含视图)宽度的大小?使用上面的示例作为参考 - "test text" 会占用更多的视图,而 "TRYING OUT AUTO SIZING" 会占用更少的视图。

注意:黑框表示其他元素。

期望的目标是一个元素(标签和/或包含视图),当文本在 运行 时更改时,字体大小根据分配的文本和可用宽度设置(这保持不变构建后)。这样所有文本在字符串中都是可见的,并且它使用可用的宽度。

目的是在缩放比例差异很大的各种设备上支持多个平台。

尝试了什么?基于惯用语 (phone/tablet/etc) 的 NamedSizes 并基于 OS(平台,即 IOS、Android 等)通过乘法和除法来操作它们。这不是最佳实践,必须有一种方法来实现这一点。

遵循 "fitting text to available size" 或 "empirically fitting text" 的 Xamarin.Forms 指南会产生与预期不同的结果。"CH5: Dealing with sizes"

请就最佳做法提出建议 and/or 后续步骤。

结构

struct FontCalc
{
    public FontCalc(Label label, double fontSize, double containerWidth)
        : this()
    {
        // Save the font size.
        FontSize = fontSize;

        // Recalculate the Label height.
        label.FontSize = fontSize;
        SizeRequest sizeRequest =
            label.Measure(containerWidth, Double.PositiveInfinity);

        // Save that height.
        TextHeight = sizeRequest.Request.Height;
    }

    public double FontSize { private set; get; }

    public double TextHeight { private set; get; }
}

实施

标签标签;

public EmpiricalFontSizePage()
{
    label = new Label();

    Padding = new Thickness(0, Device.RuntimePlatform == Device.iOS ? 30 : 0, 0, 0);
    ContentView contentView = new ContentView
    {
        Content = label
    };
    contentView.SizeChanged += OnContentViewSizeChanged;
    Content = contentView;
}

void OnContentViewSizeChanged(object sender, EventArgs args)
{
    // Get View whose size is changing.
    View view = (View)sender;

    if (view.Width <= 0 || view.Height <= 0)
        return;

    label.Text =
        "This is text displayed. Does it work?";

    // Calculate the height of the rendered text.
    FontCalc lowerFontCalc = new FontCalc(label, 10, view.Width);
    FontCalc upperFontCalc = new FontCalc(label, 100, view.Width);

    while (upperFontCalc.FontSize - lowerFontCalc.FontSize > 1)
    {
        // Get the average font size of the upper and lower bounds.
        double fontSize = (lowerFontCalc.FontSize + upperFontCalc.FontSize) / 2;

        // Check the new text height against the container height.
        FontCalc newFontCalc = new FontCalc(label, fontSize, view.Width);

        if (newFontCalc.TextHeight > view.Height)
        {
            upperFontCalc = newFontCalc;
        }
        else
        {
            lowerFontCalc = newFontCalc;
        }
    }

    // Set the final font size and the text with the embedded value.
    label.FontSize = lowerFontCalc.FontSize;
    label.Text = label.Text.Replace("??", label.FontSize.ToString("F0"));
}

(来自上面链接的 XF 文档的实现代码)

一种解决方案是使用 NuGet 包:Forms9Patch

使用它的 LinesAutoFit 属性,我们可以达到我们想要的结果。在我们的例子中,我们想要一行并且我们希望我们的 FontSize 设置为使我们的文本适合一行所需的内容。 Lines = 1AutoFit = Forms9Patch.AutoFit.WidthFontSize 设置为较大的值(如果设置 AutoFitWidth 会缩小字体,那么它是否远远超过我们期望的最大值并不重要till to what is required to make text fit within our specified number of lines) 导致 Label 自动将其 FontSize 调整为给定文本长度可用的最大值 space。