Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6538344
  • 博文数量: 915
  • 博客积分: 17977
  • 博客等级: 上将
  • 技术积分: 8846
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-26 09:59
个人简介

一个好老好老的老程序员了。

文章分类

全部博文(915)

文章存档

2022年(9)

2021年(13)

2020年(10)

2019年(40)

2018年(88)

2017年(130)

2015年(5)

2014年(12)

2013年(41)

2012年(36)

2011年(272)

2010年(1)

2009年(53)

2008年(65)

2007年(47)

2006年(81)

2005年(12)

分类: Android平台

2018-06-13 21:48:20

经验拟合文本
在具有特定尺寸的矩形内拟合文本的另一种方法涉及基于特定字体大小凭经验确定呈现文本的大小,然后向上或向下调整该字体大小。 无论可访问性设置如何,此方法都具有在所有设备上工作的优势。
但是这个过程可能很棘手:第一个问题是字体大小和渲染文本的高度之间没有干净的线性关系。 随着文本相对于其容器的宽度变大,会导致更多的换行符,并带来更多的空间浪费。 寻找最佳字体大小的计算通常涉及缩小该值的循环。
第二个问题涉及获取以特定字体大小呈现的标签大小的实际机制。 您可以在Label上设置SizeChanged处理程序,但在该处理程序中,您不希望进行任何更改(如设置新的FontSize属性),这会导致对该处理程序的递归调用。
更好的方法是调用由VisualElement定义的GetSizeRequest方法,并由Label和所有其他视图继承。 GetSizeRequest需要两个参数 - 宽度约束和高度约束。 这些值表示要在其中拟合元素的矩形的大小,其中一个或另一个可以是无限大。 通过标签使用GetSizeRequest时,通常将宽度约束参数设置为容器的宽度,将高度约束设置为Double.PositiveInfinity。
GetSizeRequest方法返回一个SizeRequest类型的值,这是一个带有两个适当的结构的结构,名为Request和Minimum,都是Size类型。 Request属性表示呈现文本的大小。 (关于这个和相关方法的更多信息可以在第26章中找到。)
EmpiricalFontSize项目演示了这种技术。 为了方便起见,它定义了一个名为FontCalc的小型结构,其构造函数针对特定Label(已用文本初始化)调用GetSizeRequest,试用字体大小和文本宽度:

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.GetSizeRequest(containerWidth, Double.PositiveInfinity);
        // Save that height.
        TextHeight = sizeRequest.Request.Height;
    }
    public double FontSize { private set; get; }
    public double TextHeight { private set; get; }
}

渲染标签的合成高度保存在TextHeight属性中。
当您在页面或布局上调用GetSizeRequest时,页面或布局需要通过可视化树来查看所有子项的大小。 这当然会带来性能上的损失,所以除非必要,否则应该避免拨打电话。 但是一个Label没有孩子,所以在一个Label上调用GetSizeRequest并不是那么糟糕。 但是,您仍然应该尝试优化呼叫。 避免循环访问一系列字体大小值,以确定不会导致文本超过容器高度的最大值。 在算法上缩小最佳值的过程更好。
GetSizeRequest要求该元素是可视化树的一部分,并且布局过程至少部分开始。 不要在页面类的构造函数中调用GetSizeRequest。 你不会从中获得信息。 第一个合理的机会是覆盖页面的OnAppearing方法。 当然,您目前可能没有足够的信息将参数传递给GetSizeRequest方法。
但是,调用GetSizeRequest并没有任何副作用。 它不会在元素上设置新的大小,这意味着它不会引发SizeChanged事件,这意味着调用SizeChanged处理程序是安全的。
EmpiricalFontSizePage类实例化承载标签的ContentView的SizeChanged处理程序中的FontCalc值。 每个FontCalc值的构造函数在Label上调用GetSize?Request调用并保存结果TextHeight。 SizeChanged处理程序的初始字体大小为10和100,假定最佳值位于这两者之间,并且它们代表下限和上限。 因此变量名称更低?FontCalc和upperFontCalc:

public class EmpiricalFontSizePage : ContentPage
{
    Label label;
    public EmpiricalFontSizePage()
    {
        label = new Label();
        Padding = new Thickness(0, Device.OnPlatform(20, 0, 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 a paragraph of text displayed with " +
                 "a FontSize value of ?? that is empirically " +
                 "calculated in a loop within the SizeChanged " +
                 "handler of the Label's container. This technique " +
                 "can be tricky: You don't want to get into " +
                 "an infinite loop by triggering a layout pass " +
                 "with every calculation. 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"));
    }
}

在while循环的每次迭代中,这两个FontCalc值的FontSize属性都会保存并获得新的FontCalc。 这成为新的lowerFontCalc或upperFontCalc值,具体取决于渲染文本的高度。 当计算的字体大小在最佳值的一个单位内时,循环结束。
大约七次迭代的循环就足以得到一个明显好于早期程序中计算的估计值的值:

将手机横向移动触发另一次重新计算,导致类似的(尽管不一定相同)字体大小:

看起来,除了简单地将FontSize属性与FontCalc的较低和较高值平均以外,算法可以得到改进。 但是字体大小和文本高度之间的关系相当复杂,有时候最简单的方法也是一样的好。

阅读(1699) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~