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
:
Color
a nastavte jeho barvu.CornerRadius
a nastavte jeho rohový poloměr.WidthRequest
nastavit šířkuBoxView
jednotek nezávislých na zařízení.HeightRequest
nastavit výškuBoxView
.
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 BoxView
rohu .
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:
Pokud jsou ze značky odebrány VerticalOptions
BoxView
vlastnosti HorizontalOptions
nebo jsou nastaveny Fill
na , 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
:
Stylové záhlaví v horní části stránky je dosaženo, AbsoluteLayout
jehož podřízené prvky jsou čtyři BoxView
prvky a Label
vš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 BoxView
na . 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 StackLayout
v 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 StackLayout
Label
:
<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:
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: BoxView
a 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:
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:
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:
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
AbsoluteLayout
pro 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 AbsoluteLayout
a 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.