Sdílet prostřednictvím


Xamarin.Forms BoxView

BoxView zobrazí jednoduchý obdélník zadané šířky, výšky a barvy. Můžete použít BoxView pro dekoraci, základní grafiku a pro interakci s uživatelem prostřednictvím dotykového ovládání.

Vzhledem k tomu Xamarin.Forms , že nemá integrovaný vektorový grafický systém, BoxView pomáhá kompenzovat. Některé ukázkové programy popsané v tomto článku používají BoxView k vykreslování grafiky. Velikost BoxView může být podobná přímce určité šířky a tloušťky a pak otočena libovolným úhlem Rotation pomocí vlastnosti.

I když BoxView může napodobovat jednoduchou grafiku, možná budete chtít prozkoumat použití SkiaSharp v Xamarin.Forms případě složitějších grafických požadavků.

Nastavení barvy a velikosti BoxView

Obvykle nastavíte následující vlastnosti BoxView:

Vlastnost Color je typu Color; vlastnost lze nastavit na libovolnou Color hodnotu, včetně 141 statických polí jen pro čtení pojmenovaných barev od abecedy od AliceBlue do YellowGreen.

Vlastnost CornerRadius je typu CornerRadius; vlastnost může být nastavena na jednu double jednotnou hodnotu poloměru rohů nebo strukturu definovanou CornerRadius čtyřmi double hodnotami, které se použijí v levém horním rohu, vpravo nahoře, v levém dolním a dolním BoxViewrohu .

HeightRequest Vlastnosti a vlastnosti WidthRequest hrají roli pouze v případě, že BoxView není v rozložení konzistence. To je případ, kdy kontejner rozložení potřebuje znát velikost dítěte, například když BoxView je podřízená buňka s automatickou velikostí v Grid rozložení. A BoxView je také unconstrained when its HorizontalOptions and VerticalOptions properties are set to values other than LayoutOptions.Fill. BoxView Pokud je nezatrénovaný, ale WidthRequest vlastnosti HeightRequest nejsou nastavené, pak je šířka nebo výška nastavená na výchozí hodnoty 40 jednotek nebo přibližně 1/4 palce na mobilních zařízeních.

Vlastnosti WidthRequest jsou HeightRequest ignorovány, pokud BoxView je omezena v rozložení, v takovém případě kontejner rozložení ukládá svou vlastní velikost na objektu BoxView.

A BoxView může být omezena v jedné dimenzi a nekontrénována v druhé. Pokud je například BoxView podřízeným objektem svislého objektu StackLayout, je svislá dimenze BoxView bez omezení a její vodorovná dimenze je obecně omezena. Existují však výjimky pro tuto vodorovnou dimenzi: Pokud BoxView má vlastnost HorizontalOptions nastavenou na něco jiného než LayoutOptions.Fill, pak je vodorovná dimenze také nekontrénovaná. Je také možné StackLayout , aby měl sám sebe nekontrénovanou vodorovnou dimenzi, v takovém případě BoxView bude také vodorovně unconstrained.

V ukázce se uprostřed stránky zobrazí unconstrained BoxView one-inch-square:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:BasicBoxView"
             x:Class="BasicBoxView.MainPage">

    <BoxView Color="CornflowerBlue"
             CornerRadius="10"
             WidthRequest="160"
             HeightRequest="160"
             VerticalOptions="Center"
             HorizontalOptions="Center" />

</ContentPage>

Tady je výsledek:

Basic BoxView

Pokud jsou ze značky odebrány VerticalOptions BoxView vlastnosti HorizontalOptions nebo jsou nastaveny Fillna , BoxView bude omezena velikostí stránky a rozbalí se tak, aby vyplnila stránku.

A BoxView může být také dítětem AbsoluteLayout. V takovém případě se umístění i velikost BoxView nastaví pomocí LayoutBounds připojené bindable vlastnosti. Popisuje se AbsoluteLayout v článku AbsoluteLayout.

Příklady všech těchto případů uvidíte v ukázkových programech, které následují.

Vykreslování textových dekorací

Můžete použít BoxView k přidání jednoduchých dekorací na stránkách ve formě vodorovných a svislých čar. Ukázka to demonstruje. Všechny vizuály programu jsou definovány v souboru MainPage.xaml , který obsahuje několik Label prvků BoxView v tomto příkladu StackLayout :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TextDecoration"
             x:Class="TextDecoration.MainPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness">
            <On Platform="iOS" Value="0, 20, 0, 0" />
        </OnPlatform>
    </ContentPage.Padding>

    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="BoxView">
                <Setter Property="Color" Value="Black" />
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>

    <ScrollView Margin="15">
        <StackLayout>

            ···

        </StackLayout>
    </ScrollView>
</ContentPage>

Všechny následující značky jsou podřízeny StackLayout. Tento kód se skládá z několika typů dekorativních BoxView prvků používaných s prvkem Label :

Dekorace textu

Stylové záhlaví v horní části stránky je dosaženo, AbsoluteLayout jehož podřízené prvky jsou čtyři BoxView prvky a Labelvšechny z nich mají přiřazená specifická umístění a velikosti:

<AbsoluteLayout>
    <BoxView AbsoluteLayout.LayoutBounds="0, 10, 200, 5" />
    <BoxView AbsoluteLayout.LayoutBounds="0, 20, 200, 5" />
    <BoxView AbsoluteLayout.LayoutBounds="10, 0, 5, 65" />
    <BoxView AbsoluteLayout.LayoutBounds="20, 0, 5, 65" />
    <Label Text="Stylish Header"
           FontSize="24"
           AbsoluteLayout.LayoutBounds="30, 25, AutoSize, AutoSize"/>
</AbsoluteLayout>

V souboru AbsoluteLayout XAML následuje Label formátovaný text, který popisuje AbsoluteLayout.

Textový řetězec můžete podtrhnout uzavřením Label hodnoty StackLayout BoxView, která má jeho HorizontalOptions hodnotu nastavenou na jinou hodnotu než Fill. Šířka StackLayout je pak řízena šířkou Label, která pak ukládá tuto šířku BoxViewna . Je BoxView přiřazena pouze explicitní výška:

<StackLayout HorizontalOptions="Center">
    <Label Text="Underlined Text"
           FontSize="24" />
    <BoxView HeightRequest="2" />
</StackLayout>

Tuto techniku nemůžete použít k podtržení jednotlivých slov v delších textových řetězcích nebo odstavci.

Je také možné použít BoxView podobný element HTML hr (vodorovné pravidlo). Jednoduše nechte šířku BoxView nadřazeného kontejneru určit, což je StackLayoutv tomto případě :

<BoxView HeightRequest="3" />

Nakonec můžete nakreslit svislou čáru na jednu stranu odstavce textu uzavřením BoxView Label vodorovného i vodorovného StackLayoutčáry . V tomto případě je výška BoxView stejné jako výška , která se řídí výškou StackLayoutLabel:

<StackLayout Orientation="Horizontal">
    <BoxView WidthRequest="4"
             Margin="0, 0, 10, 0" />
    <Label>

        ···

    </Label>
</StackLayout>

Výpis barev pomocí BoxView

Tato BoxView možnost je vhodná pro zobrazení barev. Tento program používá ListView seznam všech veřejných statických Xamarin.FormsColor polí jen pro čtení struktury:

Barvy ListView

Ukázkový program obsahuje třídu s názvem NamedColor. Statický konstruktor používá reflexi pro přístup ke všem polím Color struktury a k vytvoření objektu NamedColor pro každý z nich. Jsou uloženy ve statické All vlastnosti:

public class NamedColor
{
    // Instance members.
    private NamedColor()
    {
    }

    public string Name { private set; get; }

    public string FriendlyName { private set; get; }

    public Color Color { private set; get; }

    public string RgbDisplay { private set; get; }

    // Static members.
    static NamedColor()
    {
        List<NamedColor> all = new List<NamedColor>();
        StringBuilder stringBuilder = new StringBuilder();

        // Loop through the public static fields of the Color structure.
        foreach (FieldInfo fieldInfo in typeof(Color).GetRuntimeFields ())
        {
            if (fieldInfo.IsPublic &&
                fieldInfo.IsStatic &&
                fieldInfo.FieldType == typeof (Color))
            {
                // Convert the name to a friendly name.
                string name = fieldInfo.Name;
                stringBuilder.Clear();
                int index = 0;

                foreach (char ch in name)
                {
                    if (index != 0 && Char.IsUpper(ch))
                    {
                        stringBuilder.Append(' ');
                    }
                    stringBuilder.Append(ch);
                    index++;
                }

                // Instantiate a NamedColor object.
                Color color = (Color)fieldInfo.GetValue(null);

                NamedColor namedColor = new NamedColor
                {
                    Name = name,
                    FriendlyName = stringBuilder.ToString(),
                    Color = color,
                    RgbDisplay = String.Format("{0:X2}-{1:X2}-{2:X2}",
                                               (int)(255 * color.R),
                                               (int)(255 * color.G),
                                               (int)(255 * color.B))
                };

                // Add it to the collection.
                all.Add(namedColor);
            }
        }
        all.TrimExcess();
        All = all;
    }

    public static IList<NamedColor> All { private set; get; }
}

Vizuály programu jsou popsány v souboru XAML. Vlastnost ItemsSource ListView je nastavena na statickou NamedColor.All vlastnost, což znamená, že ListView zobrazí všechny jednotlivé NamedColor objekty:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ListViewColors"
             x:Class="ListViewColors.MainPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness">
            <On Platform="iOS" Value="10, 20, 10, 0" />
            <On Platform="Android, UWP" Value="10, 0" />
        </OnPlatform>
    </ContentPage.Padding>

    <ListView SeparatorVisibility="None"
              ItemsSource="{x:Static local:NamedColor.All}">
        <ListView.RowHeight>
            <OnPlatform x:TypeArguments="x:Int32">
                <On Platform="iOS, Android" Value="80" />
                <On Platform="UWP" Value="90" />
            </OnPlatform>
        </ListView.RowHeight>

        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <ContentView Padding="5">
                        <Frame OutlineColor="Accent"
                               Padding="10">
                            <StackLayout Orientation="Horizontal">
                                <BoxView Color="{Binding Color}"
                                         WidthRequest="50"
                                         HeightRequest="50" />
                                <StackLayout>
                                    <Label Text="{Binding FriendlyName}"
                                           FontSize="22"
                                           VerticalOptions="StartAndExpand" />
                                    <Label Text="{Binding RgbDisplay, StringFormat='RGB = {0}'}"
                                           FontSize="16"
                                           VerticalOptions="CenterAndExpand" />
                                </StackLayout>
                            </StackLayout>
                        </Frame>
                    </ContentView>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

Objekty NamedColor jsou formátovány objektem ViewCell , který je nastaven jako datová šablona objektu ListView. Tato šablona obsahuje vlastnost BoxView , jejíž Color vlastnost je vázána Color na vlastnost objektu NamedColor .

Hraní hry života podtřídou BoxView

Game of Life je mobilní automat vynalezený matematikem Johnem Conwayem a populární na stránkách Vědecké ameriky v 1970s. Dobrým úvodem je článek Wikipedie Conway game of Life.

Ukázkový Xamarin.Forms program definuje třídu s názvem LifeCell , která je odvozena od BoxView. Tato třída zapouzdřuje logiku jednotlivé buňky ve hře Život:

class LifeCell : BoxView
{
    bool isAlive;

    public event EventHandler Tapped;

    public LifeCell()
    {
        BackgroundColor = Color.White;

        TapGestureRecognizer tapGesture = new TapGestureRecognizer();
        tapGesture.Tapped += (sender, args) =>
        {
            Tapped?.Invoke(this, EventArgs.Empty);
        };
        GestureRecognizers.Add(tapGesture);
    }

    public int Col { set; get; }

    public int Row { set; get; }

    public bool IsAlive
    {
        set
        {
            if (isAlive != value)
            {
                isAlive = value;
                BackgroundColor = isAlive ? Color.Black : Color.White;
            }
        }
        get
        {
            return isAlive;
        }
    }
}

LifeCell přidá tři další vlastnosti: BoxViewa Col Row vlastnosti ukládají pozici buňky v mřížce a IsAlive vlastnost označuje její stav. Vlastnost IsAlive také nastaví Color vlastnost BoxView na černou, pokud je buňka naživu, a bílá, pokud buňka není aktivní.

LifeCell také nainstaluje TapGestureRecognizer , aby uživatel mohl přepnout stav buněk klepnutím na ně. Třída přeloží Tapped událost z rozpoznávání gest do své vlastní Tapped události.

Program GameOfLife obsahuje LifeGrid také třídu, která zapouzdřuje většinu logiky hry, a MainPage třídu, která zpracovává vizuály programu. Patří sem překryvná vrstva, která popisuje pravidla hry. Tady je program v akci zobrazující několik stovek LifeCell objektů na stránce:

Game of Life

Vytvoření digitálních hodin

Ukázkový program vytvoří 210 BoxView prvků pro simulaci tečky staromódního zobrazení 5 po 7 tečkách. Čas si můžete přečíst v režimu na výšku nebo na šířku, ale je větší na šířku:

Dot-Matrix Clock

Soubor XAML dělá o něco víc, než vytvoří instanci AbsoluteLayout použitého pro hodiny:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DotMatrixClock"
             x:Class="DotMatrixClock.MainPage"
             Padding="10"
             SizeChanged="OnPageSizeChanged">

    <AbsoluteLayout x:Name="absoluteLayout"
                    VerticalOptions="Center" />
</ContentPage>

Všechno ostatní se vyskytuje v souboru kódu. Logika zobrazení dot-matice je značně zjednodušena definicí několika polí, které popisují tečky odpovídající každé z 10 číslic a dvojtečky:

public partial class MainPage : ContentPage
{
    // Total dots horizontally and vertically.
    const int horzDots = 41;
    const int vertDots = 7;

    // 5 x 7 dot matrix patterns for 0 through 9.
    static readonly int[, ,] numberPatterns = new int[10, 7, 5]
    {
        {
            { 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 1, 0, 0, 1, 1}, { 1, 0, 1, 0, 1},
            { 1, 1, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
        },
        {
            { 0, 0, 1, 0, 0}, { 0, 1, 1, 0, 0}, { 0, 0, 1, 0, 0}, { 0, 0, 1, 0, 0},
            { 0, 0, 1, 0, 0}, { 0, 0, 1, 0, 0}, { 0, 1, 1, 1, 0}
        },
        {
            { 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 0, 0, 0, 0, 1}, { 0, 0, 0, 1, 0},
            { 0, 0, 1, 0, 0}, { 0, 1, 0, 0, 0}, { 1, 1, 1, 1, 1}
        },
        {
            { 1, 1, 1, 1, 1}, { 0, 0, 0, 1, 0}, { 0, 0, 1, 0, 0}, { 0, 0, 0, 1, 0},
            { 0, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
        },
        {
            { 0, 0, 0, 1, 0}, { 0, 0, 1, 1, 0}, { 0, 1, 0, 1, 0}, { 1, 0, 0, 1, 0},
            { 1, 1, 1, 1, 1}, { 0, 0, 0, 1, 0}, { 0, 0, 0, 1, 0}
        },
        {
            { 1, 1, 1, 1, 1}, { 1, 0, 0, 0, 0}, { 1, 1, 1, 1, 0}, { 0, 0, 0, 0, 1},
            { 0, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
        },
        {
            { 0, 0, 1, 1, 0}, { 0, 1, 0, 0, 0}, { 1, 0, 0, 0, 0}, { 1, 1, 1, 1, 0},
            { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
        },
        {
            { 1, 1, 1, 1, 1}, { 0, 0, 0, 0, 1}, { 0, 0, 0, 1, 0}, { 0, 0, 1, 0, 0},
            { 0, 1, 0, 0, 0}, { 0, 1, 0, 0, 0}, { 0, 1, 0, 0, 0}
        },
        {
            { 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0},
            { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 0}
        },
        {
            { 0, 1, 1, 1, 0}, { 1, 0, 0, 0, 1}, { 1, 0, 0, 0, 1}, { 0, 1, 1, 1, 1},
            { 0, 0, 0, 0, 1}, { 0, 0, 0, 1, 0}, { 0, 1, 1, 0, 0}
        },
    };

    // Dot matrix pattern for a colon.
    static readonly int[,] colonPattern = new int[7, 2]
    {
        { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 }, { 1, 1 }, { 1, 1 }, { 0, 0 }
    };

    // BoxView colors for on and off.
    static readonly Color colorOn = Color.Red;
    static readonly Color colorOff = new Color(0.5, 0.5, 0.5, 0.25);

    // Box views for 6 digits, 7 rows, 5 columns.
    BoxView[, ,] digitBoxViews = new BoxView[6, 7, 5];

    ···

}

Tato pole končí trojrozměrnou maticí BoxView prvků pro uložení tečkovaných vzorů pro šest číslic.

Konstruktor vytvoří všechny BoxView prvky pro číslice a dvojtečku a také inicializuje Color vlastnost BoxView prvků dvojtečky:

public partial class MainPage : ContentPage
{

    ···

    public MainPage()
    {
        InitializeComponent();

        // BoxView dot dimensions.
        double height = 0.85 / vertDots;
        double width = 0.85 / horzDots;

        // Create and assemble the BoxViews.
        double xIncrement = 1.0 / (horzDots - 1);
        double yIncrement = 1.0 / (vertDots - 1);
        double x = 0;

        for (int digit = 0; digit < 6; digit++)
        {
            for (int col = 0; col < 5; col++)
            {
                double y = 0;

                for (int row = 0; row < 7; row++)
                {
                    // Create the digit BoxView and add to layout.
                    BoxView boxView = new BoxView();
                    digitBoxViews[digit, row, col] = boxView;
                    absoluteLayout.Children.Add(boxView,
                                                new Rectangle(x, y, width, height),
                                                AbsoluteLayoutFlags.All);
                    y += yIncrement;
                }
                x += xIncrement;
            }
            x += xIncrement;

            // Colons between the hours, minutes, and seconds.
            if (digit == 1 || digit == 3)
            {
                int colon = digit / 2;

                for (int col = 0; col < 2; col++)
                {
                    double y = 0;

                    for (int row = 0; row < 7; row++)
                    {
                        // Create the BoxView and set the color.
                        BoxView boxView = new BoxView
                            {
                                Color = colonPattern[row, col] == 1 ?
                                            colorOn : colorOff
                            };
                        absoluteLayout.Children.Add(boxView,
                                                    new Rectangle(x, y, width, height),
                                                    AbsoluteLayoutFlags.All);
                        y += yIncrement;
                    }
                    x += xIncrement;
                }
                x += xIncrement;
            }
        }

        // Set the timer and initialize with a manual call.
        Device.StartTimer(TimeSpan.FromSeconds(1), OnTimer);
        OnTimer();
    }

    ···

}

Tento program používá relativní umístění a určení velikosti funkce AbsoluteLayout. Šířka a výška každého BoxView jsou nastavena na desetinné hodnoty, konkrétně 85 % z 1 děleno počtem vodorovných a svislých bodů. Pozice jsou také nastaveny na desetinné hodnoty.

Vzhledem k tomu, že všechny pozice a velikosti jsou relativní k celkové velikosti AbsoluteLayout, SizeChanged obslužná rutina stránky potřebuje pouze nastavit HeightRequest :AbsoluteLayout

public partial class MainPage : ContentPage
{

    ···

    void OnPageSizeChanged(object sender, EventArgs args)
    {
        // No chance a display will have an aspect ratio > 41:7
        absoluteLayout.HeightRequest = vertDots * Width / horzDots;
    }

    ···

}

Šířka stránky AbsoluteLayout se nastaví automaticky, protože se roztáhne na plnou šířku stránky.

Konečný kód ve MainPage třídě zpracovává zpětné volání časovače a barvy tečky každé číslice. Definice multidimenzionálních polí na začátku souboru s kódem pomáhá, aby tato logika byla nejjednodušší součástí programu:

public partial class MainPage : ContentPage
{

    ···

    bool OnTimer()
    {
        DateTime dateTime = DateTime.Now;

        // Convert 24-hour clock to 12-hour clock.
        int hour = (dateTime.Hour + 11) % 12 + 1;

        // Set the dot colors for each digit separately.
        SetDotMatrix(0, hour / 10);
        SetDotMatrix(1, hour % 10);
        SetDotMatrix(2, dateTime.Minute / 10);
        SetDotMatrix(3, dateTime.Minute % 10);
        SetDotMatrix(4, dateTime.Second / 10);
        SetDotMatrix(5, dateTime.Second % 10);
        return true;
    }

    void SetDotMatrix(int index, int digit)
    {
        for (int row = 0; row < 7; row++)
            for (int col = 0; col < 5; col++)
            {
                bool isOn = numberPatterns[digit, row, col] == 1;
                Color color = isOn ? colorOn : colorOff;
                digitBoxViews[index, row, col].Color = color;
            }
    }
}

Vytvoření analogových hodin

Zdá se, že tečkované hodiny mohou být zřejmé použití BoxView, ale BoxView prvky jsou také schopné realizovat analogové hodiny:

Hodiny BoxView

Všechny vizuály v ukázkovém programu jsou podřízené objektu AbsoluteLayout. Tyto prvky mají velikost pomocí LayoutBounds připojené vlastnosti a otočené pomocí Rotation vlastnosti.

V souboru XAML se vytvoří tři BoxView prvky pro ruce hodin, ale nejsou umístěné nebo mají velikost:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:BoxViewClock"
             x:Class="BoxViewClock.MainPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness">
            <On Platform="iOS" Value="0, 20, 0, 0" />
        </OnPlatform>
    </ContentPage.Padding>

    <AbsoluteLayout x:Name="absoluteLayout"
                    SizeChanged="OnAbsoluteLayoutSizeChanged">

        <BoxView x:Name="hourHand"
                 Color="Black" />

        <BoxView x:Name="minuteHand"
                 Color="Black" />

        <BoxView x:Name="secondHand"
                 Color="Black" />
    </AbsoluteLayout>
</ContentPage>

Konstruktor souboru kódu vytvoří instanci 60 BoxView prvků pro značky značek kolem obvodu hodin:

public partial class MainPage : ContentPage
{

    ···

    BoxView[] tickMarks = new BoxView[60];

    public MainPage()
    {
        InitializeComponent();

        // Create the tick marks (to be sized and positioned later).
        for (int i = 0; i < tickMarks.Length; i++)
        {
            tickMarks[i] = new BoxView { Color = Color.Black };
            absoluteLayout.Children.Add(tickMarks[i]);
        }

        Device.StartTimer(TimeSpan.FromSeconds(1.0 / 60), OnTimerTick);
    }

    ···

}

Velikost a umístění všech BoxView prvků se vyskytuje v obslužné rutině SizeChanged AbsoluteLayoutpro objekt . Malá struktura vnitřní pro třídu volaná HandParams popisuje velikost každé ze tří rukou vzhledem k celkové velikosti hodin:

public partial class MainPage : ContentPage
{
    // Structure for storing information about the three hands.
    struct HandParams
    {
        public HandParams(double width, double height, double offset) : this()
        {
            Width = width;
            Height = height;
            Offset = offset;
        }

        public double Width { private set; get; }   // fraction of radius
        public double Height { private set; get; }  // ditto
        public double Offset { private set; get; }  // relative to center pivot
    }

    static readonly HandParams secondParams = new HandParams(0.02, 1.1, 0.85);
    static readonly HandParams minuteParams = new HandParams(0.05, 0.8, 0.9);
    static readonly HandParams hourParams = new HandParams(0.125, 0.65, 0.9);

    ···

 }

Obslužná rutina SizeChanged určuje střed a poloměr AbsoluteLayouta pak velikost a umístění 60 BoxView prvků použitých jako značky značek. Smyčka for končí nastavením Rotation vlastnosti každého z těchto BoxView prvků. Na konci SizeChanged obslužné rutiny LayoutHand je volána metoda pro velikost a umístění tří rukou hodin:

public partial class MainPage : ContentPage
{

    ···

    void OnAbsoluteLayoutSizeChanged(object sender, EventArgs args)
    {
        // Get the center and radius of the AbsoluteLayout.
        Point center = new Point(absoluteLayout.Width / 2, absoluteLayout.Height / 2);
        double radius = 0.45 * Math.Min(absoluteLayout.Width, absoluteLayout.Height);

        // Position, size, and rotate the 60 tick marks.
        for (int index = 0; index < tickMarks.Length; index++)
        {
            double size = radius / (index % 5 == 0 ? 15 : 30);
            double radians = index * 2 * Math.PI / tickMarks.Length;
            double x = center.X + radius * Math.Sin(radians) - size / 2;
            double y = center.Y - radius * Math.Cos(radians) - size / 2;
            AbsoluteLayout.SetLayoutBounds(tickMarks[index], new Rectangle(x, y, size, size));
            tickMarks[index].Rotation = 180 * radians / Math.PI;
        }

        // Position and size the three hands.
        LayoutHand(secondHand, secondParams, center, radius);
        LayoutHand(minuteHand, minuteParams, center, radius);
        LayoutHand(hourHand, hourParams, center, radius);
    }

    void LayoutHand(BoxView boxView, HandParams handParams, Point center, double radius)
    {
        double width = handParams.Width * radius;
        double height = handParams.Height * radius;
        double offset = handParams.Offset;

        AbsoluteLayout.SetLayoutBounds(boxView,
            new Rectangle(center.X - 0.5 * width,
                          center.Y - offset * height,
                          width, height));

        // Set the AnchorY property for rotations.
        boxView.AnchorY = handParams.Offset;
    }

    ···

}

Metoda LayoutHand určuje velikost a umístění každé ruky tak, aby ukazovaly rovnou do pozice 12:00. Na konci metody AnchorY je vlastnost nastavena na pozici odpovídající středu hodin. To označuje střed otáčení.

Ruce se otočí ve funkci zpětného volání časovače:

public partial class MainPage : ContentPage
{

    ···

    bool OnTimerTick()
    {
        // Set rotation angles for hour and minute hands.
        DateTime dateTime = DateTime.Now;
        hourHand.Rotation = 30 * (dateTime.Hour % 12) + 0.5 * dateTime.Minute;
        minuteHand.Rotation = 6 * dateTime.Minute + 0.1 * dateTime.Second;

        // Do an animation for the second hand.
        double t = dateTime.Millisecond / 1000.0;

        if (t < 0.5)
        {
            t = 0.5 * Easing.SpringIn.Ease(t / 0.5);
        }
        else
        {
            t = 0.5 * (1 + Easing.SpringOut.Ease((t - 0.5) / 0.5));
        }

        secondHand.Rotation = 6 * (dateTime.Second + t);
        return true;
    }
}

Druhá ruka se zpracovává trochu jinak: Funkce pro usnadnění animace se použije k tomu, aby pohyb vypadal mechanicky, a ne hladce. Na každém klíště, druhá ruka se trochu vrátí a pak přehodí jeho cíl. Tento malý bit kódu přidává hodně do realismu pohybu.