天天看點

第二十一章:變換(十)

樣式通過将AnchorX值設定為0來結束,該值将旋轉中心設定為每個Label的左邊緣的垂直中心。 然後每個Label都會獲得一個獨特的旋轉設定:

第二十一章:變換(十)

顯然,選擇“ROTATE”字元串之前的空格,以便R的垂直條組合形成一個看起來幾乎像圓的16邊多邊形。

如果每個字母都是單獨的Label元素,您還可以在文本字元串中旋轉單個字母。 首先将這些Label元素放在AbsoluteLayout中,然後應用Rotation屬性使其看起來好像字母遵循特定的非線性路徑。 CircularText程式将這些字母排成一個圓圈。

CircularText是一個僅代碼程式,類似于備用BoxViewCircle算法。 構造函數負責建立所有單個Label元素并将它們添加到AbsoluteLayout的Children集合中。 在構造函數中沒有執行定位或旋轉,因為程式還不知道這些單獨的Label元素有多大,或者AbsoluteLayout有多大:

public class CircularTextPage : ContentPage
{
    AbsoluteLayout absoluteLayout;
    Label[] labels;
    public CircularTextPage()
    {
        // Create the AbsoluteLayout.
        absoluteLayout = new AbsoluteLayout();
        absoluteLayout.SizeChanged += (sender, args) =>
            {
                LayOutLabels();
            };
        Content = absoluteLayout;
        // Create the Labels.
        string text = "Xamarin.Forms makes me want to code more with ";
        labels = new Label[text.Length];
        double fontSize = 32;
        int countSized = 0;
        for (int index = 0; index < text.Length; index++)
        {
            char ch = text[index];
            Label label = new Label
                {
                    Text = ch == ' ' ? "-" : ch.ToString(),
                    Opacity = ch == ' ' ? 0 : 1,
                    FontSize = fontSize,
                };
            label.SizeChanged += (sender, args) =>
                {
                    if (++countSized >= labels.Length)
                        LayOutLabels();
                };
            labels[index] = label;
            absoluteLayout.Children.Add(label);
        }
    }
    void LayOutLabels()
    {
        // Calculate the total width of the Labels.
        double totalWidth = 0;
        foreach (Label label in labels)
        {
            totalWidth += label.Width;
        }
        // From that, get a radius of the circle to center of Labels.
        double radius = totalWidth / 2 / Math.PI + labels[0].Height / 2;
        Point center = new Point(absoluteLayout.Width / 2, absoluteLayout.Height / 2);
        double angle = 0;
        for (int index = 0; index < labels.Length; index++)
        {
            Label label = labels[index];
            // Set the position of the Label.
            double x = center.X + radius * Math.Sin(angle) - label.Width / 2;
            double y = center.Y - radius * Math.Cos(angle) - label.Height / 2;
            AbsoluteLayout.SetLayoutBounds(label, new Rectangle(x, y, AbsoluteLayout.AutoSize,
                                                                    AbsoluteLayout.AutoSize));
            // Set the rotation of the Label.
            label.Rotation = 360 * angle / 2 / Math.PI;
            // Increment the rotation angle.
            if (index < labels.Length - 1)
            {
                angle += 2 * Math.PI * (label.Width + labels[index + 1].Width) / 2 / totalWidth;
            }
        }
    }
}           

請注意建立每個Label元素的代碼:如果原始文本字元串中的字元是空格,則Label的Text屬性将配置設定一個破折号,但Opacity屬性設定為0,以便破折号不可見。這是修複Windows運作時平台上出現的問題的一個小技巧:如果Label隻包含一個空格,那麼Label的寬度計算為零,所有單詞一起運作。

所有操作都發生在LayOutLabels方法中。從構造函數中表示為lambda函數的兩個SizeChanged處理程式調用此方法。在程式啟動後或手機改變方向時,很快就會調用AbsoluteLayout的SizeChanged處理程式。

Label元素的SizeChanged處理程式跟蹤到目前為止已調整大小的數量,并且僅在LayoutLabels準備就緒時調用它們。

LayOutLabels方法計算所有Label元素的總寬度。如果假設它是圓的圓周,則該方法可以容易地計算該圓的半徑。但是這個半徑實際上是每個Label的高度的一半。是以,該半徑的端點與每個标簽的中心重合。通過從該點減去标簽寬度和高度的一半,Label位于AbsoluteLayout内。

累積角度既可用于查找下一個Label的半徑端點,也可用于旋轉Label。由于每個半徑的端點與每個Label的中心重合,是以角度将根據目前Label的寬度的一半和下一個Label的寬度的一半遞增。

雖然數學有點棘手,但結果是值得的:

第二十一章:變換(十)

此程式不會設定AnchorX和AnchorY的非預設值,是以在iOS上更改手機方向沒有問題。

繼續閱讀