Xamarin.Forms BoxView
BoxView
merender persegi panjang sederhana dari lebar, tinggi, dan warna tertentu. Anda dapat menggunakan BoxView
untuk dekorasi, grafis dasar, dan untuk interaksi dengan pengguna melalui sentuhan.
Karena Xamarin.Forms tidak memiliki sistem grafis vektor bawaan, BoxView
membantu mengkompensasi. Beberapa program sampel yang dijelaskan dalam artikel ini menggunakan BoxView
untuk merender grafik. BoxView
dapat berukuran menyerupai garis lebar dan ketebalan tertentu, lalu diputar oleh sudut apa pun menggunakan Rotation
properti .
Meskipun BoxView
dapat meniru grafik sederhana, Anda mungkin ingin menyelidiki Menggunakan SkiaSharp untuk Xamarin.Forms persyaratan grafis yang lebih canggih.
Mengatur Warna dan Ukuran BoxView
Biasanya Anda akan mengatur properti berikut dari BoxView
:
Color
untuk mengatur warnanya.CornerRadius
untuk mengatur radius sudutnya.WidthRequest
untuk mengatur lebarBoxView
dalam unit independen perangkat.HeightRequest
untuk mengatur tinggiBoxView
.
Properti Color
berjenis Color
; properti dapat diatur ke nilai apa pun Color
, termasuk 141 bidang baca-saja statis dari warna bernama mulai dari alfabet dari AliceBlue
ke YellowGreen
.
Properti CornerRadius
berjenis CornerRadius
; properti dapat diatur ke double
satu nilai radius sudut seragam, atau CornerRadius
struktur yang ditentukan oleh empat double
nilai yang diterapkan ke kiri atas, kanan atas, kiri bawah, dan kanan bawah dari BoxView
.
Properti WidthRequest
dan HeightRequest
hanya memainkan peran jika BoxView
tidak dibatasi dalam tata letak. Ini adalah kasus ketika kontainer tata letak perlu mengetahui ukuran anak, misalnya, ketika BoxView
adalah anak dari sel berukuran otomatis dalam Grid
tata letak. A BoxView
juga tidak dibatasi ketika HorizontalOptions
properti dan VerticalOptions
diatur ke nilai selain LayoutOptions.Fill
. BoxView
Jika tidak dibatasi, tetapi WidthRequest
properti dan HeightRequest
tidak diatur, maka lebar atau tinggi diatur ke nilai default 40 unit, atau sekitar 1/4 inci pada perangkat seluler.
Properti WidthRequest
dan HeightRequest
diabaikan jika BoxView
dibatasi dalam tata letak, dalam hal ini kontainer tata letak memberlakukan ukurannya sendiri pada BoxView
.
BoxView
Dapat dibatasi dalam satu dimensi dan tidak dibatasi di dimensi lainnya. Misalnya, jika BoxView
adalah anak dari vertikal StackLayout
, dimensi BoxView
vertikal tidak dibatasi dan dimensi horizontalnya umumnya dibatasi. Tetapi ada pengecualian untuk dimensi horizontal tersebut BoxView
: Jika propertinya HorizontalOptions
diatur ke sesuatu selain LayoutOptions.Fill
, maka dimensi horizontal juga tidak dibatasi. Dimungkinkan juga bagi dirinya sendiri untuk StackLayout
memiliki dimensi horizontal yang tidak dibatasi, dalam hal ini BoxView
juga akan tidak dibatasi secara horizontal.
Sampel menampilkan satu inci-persegi yang tidak dibatasi BoxView
di tengah halamannya:
<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>
Berikut hasilnya:
VerticalOptions
Jika properti dan HorizontalOptions
dihapus dari BoxView
tag atau diatur ke Fill
, maka BoxView
menjadi dibatasi oleh ukuran halaman, dan diperluas untuk mengisi halaman.
Juga BoxView
bisa menjadi anak dari AbsoluteLayout
. Dalam hal ini, lokasi dan ukuran BoxView
diatur menggunakan LayoutBounds
properti yang dapat diikat yang terpasang. dibahas AbsoluteLayout
dalam artikel AbsoluteLayout.
Anda akan melihat contoh semua kasus ini dalam program sampel berikut.
Merender Dekorasi Teks
Anda dapat menggunakan BoxView
untuk menambahkan beberapa dekorasi sederhana di halaman Anda dalam bentuk garis horizontal dan vertikal. Sampel menunjukkan ini. Semua visual program didefinisikan dalam file MainPage.xaml , yang berisi beberapa Label
elemen dan BoxView
dalam yang StackLayout
ditunjukkan di sini:
<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>
Semua markup yang mengikuti adalah anak-anak dari StackLayout
. Markup ini terdiri dari beberapa jenis elemen dekoratif BoxView
yang digunakan dengan Label
elemen :
Header bergaya di bagian atas halaman dicapai dengan AbsoluteLayout
anak-anaknya adalah empat BoxView
elemen dan Label
, yang semuanya ditetapkan lokasi dan ukuran tertentu:
<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>
Dalam file XAML, AbsoluteLayout
diikuti dengan Label
teks berformat yang menjelaskan AbsoluteLayout
.
Anda dapat menggaris bawahi string teks dengan mengapit dan Label
dalam yang nilainya HorizontalOptions
diatur ke sesuatu selain Fill
.StackLayout
BoxView
Lebar StackLayout
kemudian diatur oleh lebar Label
, yang kemudian memberlakukan lebar tersebut BoxView
pada . yang BoxView
ditetapkan hanya tinggi eksplisit:
<StackLayout HorizontalOptions="Center">
<Label Text="Underlined Text"
FontSize="24" />
<BoxView HeightRequest="2" />
</StackLayout>
Anda tidak dapat menggunakan teknik ini untuk menggaris bawahi kata-kata individual dalam string teks atau paragraf yang lebih panjang.
Anda juga dapat menggunakan BoxView
untuk menyerupai elemen HTML hr
(aturan horizontal). Cukup biarkan lebar BoxView
ditentukan oleh kontainer induknya, yang dalam hal ini adalah StackLayout
:
<BoxView HeightRequest="3" />
Terakhir, Anda dapat menggambar garis vertikal di satu sisi paragraf teks dengan mengapit BoxView
dan Label
dalam horizontal StackLayout
. Dalam hal ini, tingginya BoxView
sama dengan tinggi StackLayout
, yang diatur oleh tinggi Label
:
<StackLayout Orientation="Horizontal">
<BoxView WidthRequest="4"
Margin="0, 0, 10, 0" />
<Label>
···
</Label>
</StackLayout>
Mencantumkan Warna dengan BoxView
BoxView
nyaman untuk menampilkan warna. Program ini menggunakan untuk mencantumkan ListView
semua bidang publik statis baca-saja dari Xamarin.FormsColor
struktur:
Program sampel mencakup kelas bernama NamedColor
. Konstruktor statis menggunakan pantulan untuk mengakses semua bidang Color
struktur dan membuat NamedColor
objek untuk masing-masing bidang. Ini disimpan dalam properti statis All
:
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; }
}
Visual program dijelaskan dalam file XAML. Properti ItemsSource
diatur ListView
ke properti statis NamedColor.All
, yang berarti bahwa ListView
menampilkan semua objek individual NamedColor
:
<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>
Objek NamedColor
diformat oleh ViewCell
objek yang diatur sebagai templat data .ListView
Templat ini mencakup BoxView
properti yang Color
terikat ke Color
properti NamedColor
objek.
Memainkan Game of Life oleh Subkelas BoxView
The Game of Life adalah automaton seluler yang diciptakan oleh matematikawan John Conway dan dipopulerkan di halaman Ilmiah Amerika pada tahun 1970-an. Pengantar yang baik disediakan oleh artikel Wikipedia Conway's Game of Life.
Program Xamarin.Forms sampel mendefinisikan kelas bernama LifeCell
yang berasal dari BoxView
. Kelas ini merangkum logika sel individu dalam Game of Life:
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
menambahkan tiga properti lagi ke BoxView
: properti dan Row
menyimpan posisi sel dalam kisi, dan IsAlive
properti Col
menunjukkan statusnya. Properti IsAlive
juga mengatur Color
properti menjadi BoxView
hitam jika sel hidup, dan putih jika sel tidak hidup.
LifeCell
juga menginstal TapGestureRecognizer
untuk memungkinkan pengguna untuk mengalihkan status sel dengan mengetuknya. Kelas menerjemahkan Tapped
peristiwa dari pengenal gerakan ke dalam peristiwanya sendiri Tapped
.
Program GameOfLife juga mencakup LifeGrid
kelas yang merangkum banyak logika permainan, dan MainPage
kelas yang menangani visual program. Ini termasuk overlay yang menjelaskan aturan permainan. Berikut adalah program dalam tindakan yang menunjukkan beberapa ratus LifeCell
objek di halaman:
Membuat Jam Digital
Program sampel membuat 210 BoxView
elemen untuk mensimulasikan titik-titik tampilan matriks titik 5 demi 7 kuno. Anda dapat membaca waktu dalam mode potret atau lanskap, tetapi lebih besar dalam lanskap:
File XAML melakukan lebih sedikit daripada membuat instans yang AbsoluteLayout
digunakan untuk jam:
<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>
Segala sesuatu yang lain terjadi dalam file code-behind. Logika tampilan dot-matrix sangat disederhanakan oleh definisi beberapa array yang menjelaskan titik yang sesuai dengan masing-masing dari 10 digit dan titik dua:
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];
···
}
Bidang-bidang ini disimpulkan dengan array BoxView
elemen tiga dimensi untuk menyimpan pola titik untuk enam digit.
Konstruktor membuat semua BoxView
elemen untuk digit dan titik dua, dan juga menginisialisasi Color
properti BoxView
elemen untuk titik dua:
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();
}
···
}
Program ini menggunakan fitur AbsoluteLayout
posisi relatif dan ukuran . Lebar dan tinggi masing-masing BoxView
diatur ke nilai pecahan, khususnya 85% dari 1 dibagi dengan jumlah titik horizontal dan vertikal. Posisi juga diatur ke nilai pecahan.
Karena semua posisi dan ukuran relatif terhadap ukuran AbsoluteLayout
total , SizeChanged
handler untuk halaman hanya perlu mengatur HeightRequest
dari 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;
}
···
}
Lebar AbsoluteLayout
diatur secara otomatis karena membentang ke lebar penuh halaman.
Kode akhir di MainPage
kelas memproses panggilan balik timer dan mewarnai titik-titik dari setiap digit. Definisi array multi-dimensi di awal file code-behind membantu menjadikan logika ini sebagai bagian paling sederhana dari program:
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;
}
}
}
Membuat Jam Analog
Jam dot-matrix mungkin tampaknya merupakan aplikasi yang jelas dari BoxView
, tetapi BoxView
elemen juga mampu mewujudkan jam analog:
Semua visual dalam program sampel adalah turunan dari AbsoluteLayout
. Elemen-elemen ini berukuran LayoutBounds
menggunakan properti terlampir, dan diputar menggunakan Rotation
properti .
Tiga BoxView
elemen untuk tangan jam dibuat dalam file XAML, tetapi tidak diposisikan atau berukuran:
<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 file code-behind membuat instans 60 BoxView
elemen untuk tanda centang di sekitar lingkar jam:
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);
}
···
}
Ukuran dan posisi semua BoxView
elemen terjadi di SizeChanged
handler untuk AbsoluteLayout
. Sedikit struktur internal ke kelas yang disebut HandParams
menggambarkan ukuran masing-masing dari tiga tangan relatif terhadap ukuran total jam:
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);
···
}
Handler SizeChanged
menentukan pusat dan radius AbsoluteLayout
, lalu ukuran dan posisi 60 BoxView
elemen yang digunakan sebagai tanda centang. Perulangan for
menyimpulkan dengan mengatur Rotation
properti dari masing-masing elemen ini BoxView
. Di akhir SizeChanged
handler, LayoutHand
metode dipanggil untuk mengukur dan memposisikan tiga tangan jam:
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;
}
···
}
Metode ini LayoutHand
mengukur dan memposisikan setiap tangan untuk mengarah langsung ke posisi 12:00. Di akhir metode, AnchorY
properti diatur ke posisi yang sesuai dengan pusat jam. Ini menunjukkan pusat rotasi.
Tangan diputar dalam fungsi panggilan balik timer:
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;
}
}
Tangan kedua diperlakukan sedikit berbeda: Fungsi pelingan animasi diterapkan untuk membuat gerakan tampak mekanis daripada halus. Pada setiap centang, tangan kedua menarik kembali sedikit dan kemudian melakukan overshoot tujuannya. Sedikit kode ini menambah banyak realisme gerakan.