Xamarin.Forms BoxView
BoxView representa un rectángulo simple de un ancho, alto y color especificados. Puede usar para BoxView decoración, gráficos rudimentarios y para la interacción con el usuario a través de la interacción táctil.
Dado Xamarin.Forms que no tiene un sistema de gráficos vectoriales integrado, ayuda a BoxView compensar. Algunos de los programas de ejemplo descritos en este artículo usan BoxView para representar gráficos. Se puede ajustar el tamaño para que se parezca a una línea de un ancho y grosor específicos y, a continuación, girar por BoxView cualquier ángulo mediante la propiedad Rotation .
Aunque BoxView puede imitar gráficos sencillos, es posible que quiera investigar los Using SkiaSharp in Xamarin.Forms requisitos de gráficos más sofisticados.
Establecer el color y el tamaño de BoxView
Normalmente, establecerá las siguientes propiedades de BoxView :
- Xamarin_Forms _BoxView_Color" data-linktype="absolute-path">
Colorpara establecer su color. - Xamarin_Forms _BoxView_CornerRadius" data-linktype="absolute-path">establecer su radio
CornerRadiusde esquina. - Xamarin_Forms _VisualElement_WidthRequest" data-linktype="absolute-path">para establecer el ancho de en unidades independientes
WidthRequestBoxViewdel dispositivo. - Xamarin_Forms _VisualElement_HeightRequest" data-linktype="absolute-path">
HeightRequestpara establecer el alto deBoxView.
La propiedad es de tipo ; la propiedad se puede establecer en cualquier valor, incluidos los ColorColorColor 141 campos estáticos de solo lectura de colores con nombre que van alfabéticamente de AliceBlue a YellowGreen .
La propiedad es de tipo ; la propiedad se puede establecer en un único valor de radio de esquina uniforme o en una estructura definida por cuatro valores que se aplican a la parte superior izquierda, superior CornerRadiusCornerRadiusdoubleCornerRadiusdouble derecha, inferior izquierda e inferior derecha de BoxView .
Las WidthRequest propiedades y solo desempeñan un rol si el elemento no está HeightRequestBoxViewWidthRequest el diseño. Este es el caso cuando el contenedor de diseño necesita conocer el tamaño del elemento secundario, por ejemplo, cuando es un elemento secundario de una celda de tamaño automático BoxView en el Grid diseño. Un BoxView también no está entrenado cuando sus propiedades y se establecen en valores HorizontalOptionsVerticalOptions distintos de LayoutOptions.Fill . Si no está entrenado, pero las propiedades y no están establecidas, el ancho o alto se establecen en valores predeterminados de BoxViewWidthRequest 40 unidades o aproximadamente de 1/4 pulgadas en dispositivos HeightRequest móviles.
Las propiedades y se omiten si está restringido en el diseño, en cuyo caso el contenedor de diseño WidthRequest impone su propio tamaño en HeightRequestBoxViewWidthRequestBoxView .
Una vista BoxView se puede restringir en una dimensión y sin restricciones en la otra. Por ejemplo, si es un elemento secundario de una vertical, la dimensión vertical de no está entrenada y su dimensión horizontal suele estar BoxViewStackLayoutBoxView restringida. Pero hay excepciones para esa dimensión horizontal: si tiene su propiedad establecida en un valor distinto de , la dimensión horizontal también no está BoxViewHorizontalOptionsLayoutOptions.Fill entrenando. También es posible que el propio tenga una dimensión horizontal sin restricciones, en cuyo caso también estará horizontalmente sin StackLayoutBoxView restricciones.
El ejemplo BasicBoxView muestra un cuadrado de una pulgada sin restricciones en el centro de su página:
<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>
Este es el resultado:
Si las propiedades y se quitan de la etiqueta o se establecen en , se restringe por el tamaño de la página y se expande para VerticalOptionsHorizontalOptions rellenar la BoxViewFillBoxView página.
También BoxView puede ser un elemento secundario de AbsoluteLayout . En ese caso, tanto la ubicación como el tamaño de BoxView se establecen mediante la propiedad LayoutBounds enlazable adjunta. se AbsoluteLayout describe en el artículo AbsoluteLayout.
Verá ejemplos de todos estos casos en los programas de ejemplo siguientes.
Representación de adornos de texto
Puede usar para agregar algunas decoración sencillas en las páginas BoxView en forma de líneas horizontales y verticales. El ejemplo TextDecoration muestra esto. Todos los objetos visuales del programa se definen en el archivo MainPage.xaml, que contiene varios elementos y en el que se muestra BoxViewStackLayout aquí:
<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>
Todos los marcados siguientes son elementos secundarios de StackLayout . Este marcado consta de varios tipos de elementos BoxView decorados que se usan con el Label elemento :
El encabezado de encabezado de la parte superior de la página se logra con un cuyos elementos secundarios son cuatro elementos y , a todos los cuales se les asignan ubicaciones y tamaños AbsoluteLayoutBoxViewLabel específicos:
<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>
En el archivo XAML, va seguido de un objeto AbsoluteLayout con texto con formato que describe LabelAbsoluteLayout .
Puede subrayado una cadena de texto si incluye y en un que tiene su valor establecido en un valor LabelBoxView distinto de StackLayoutHorizontalOptionsFill . A continuación, el ancho de se rige por el ancho de , que luego StackLayout impone ese ancho en LabelBoxView . Solo BoxView se le asigna un alto explícito:
<StackLayout HorizontalOptions="Center">
<Label Text="Underlined Text"
FontSize="24" />
<BoxView HeightRequest="2" />
</StackLayout>
No se puede usar esta técnica para subrayado de palabras individuales dentro de cadenas de texto más largas o un párrafo.
También es posible usar para parecerse a BoxView un elemento HTML hr (regla horizontal). Simplemente deje que el ancho del objeto esté determinado por su contenedor primario, que BoxView en este caso es StackLayout :
<BoxView HeightRequest="3" />
Por último, puede dibujar una línea vertical en un lado de un párrafo de texto si incluye y BoxViewLabel en un StackLayout horizontal. En este caso, el alto de es el mismo que el alto de , que se BoxView rige por el alto de StackLayoutLabel :
<StackLayout Orientation="Horizontal">
<BoxView WidthRequest="4"
Margin="0, 0, 10, 0" />
<Label>
···
</Label>
</StackLayout>
Enumeración de colores con BoxView
es BoxView conveniente para mostrar colores. Este programa usa para ListView enumerar todos los campos de solo lectura estáticos públicos de la Xamarin.FormsColor estructura :
El programa ListViewColors incluye una clase denominada . El constructor estático usa la reflexión para tener acceso a todos los campos de la Color estructura y crear un objeto para cada NamedColor uno. Estos se almacenan en la propiedad All estática:
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; }
}
Los objetos visuales del programa se describen en el archivo XAML. La ItemsSource propiedad de se establece en la propiedad ListViewNamedColor.All estática, lo que significa que muestra todos ListView los objetos NamedColor individuales:
<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>
El objeto que se establece como la plantilla de datos de tiene el formato de NamedColorViewCell los objetos ListView . Esta plantilla incluye una BoxView cuya Color propiedad está enlazada a la propiedad Color del objeto NamedColor .
Playing the Game of Life by Subclassing BoxView
El juego de la vida es un autómodo móvil que el matemático John Conway ha usado y popularizado en las páginas de Scientific American en los años 70. Se proporciona una buena introducción en el artículo de Wikipedia Conway's Game of Life( Juego de vida de Conway).
El Xamarin.FormsXamarin.Forms define una clase denominada que deriva de BoxView . Esta clase encapsula la lógica de una celda individual en 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 agrega tres propiedades más a : las propiedades y almacenan la posición de la celda dentro de la cuadrícula, y la BoxViewCol propiedad indica su RowIsAlive estado. La propiedad también establece la propiedad de en negro si la celda está activa y en blanco si la IsAliveColor celda no está BoxView activa.
LifeCell también instala para permitir que el usuario alterne el estado TapGestureRecognizer de las celdas pulsando en ellas. La clase convierte el evento Tapped del reconocedor de gestos en su propio Tapped evento.
El programa GameOfLife también incluye una clase que encapsula gran parte de la lógica del juego y una clase que controla los objetos visuales del MainPage programa. Entre ellas se incluye una superposición que describe las reglas del juego. Este es el programa en acción que muestra un par de cientos LifeCell de objetos en la página:
Creación de un reloj digital
El programa DotMatrixClock crea 210 elementos para simular los puntos de una pantalla de matriz de puntos 5 por 7 antigua. Puede leer la hora en modo vertical o horizontal, pero es mayor en horizontal:
El archivo XAML hace poco más que crear una instancia del AbsoluteLayout usado para el reloj:
<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>
Todo lo demás se produce en el archivo de código subyacente. La lógica de presentación de la matriz de puntos se simplifica en gran medida mediante la definición de varias matrices que describen los puntos correspondientes a cada uno de los 10 dígitos y dos puntos:
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];
···
}
Estos campos concluyen con una matriz tridimensional de BoxView elementos para almacenar los patrones de puntos para los seis dígitos.
El constructor crea todos los elementos para los dígitos y los dos puntos, y también inicializa BoxView la propiedad de los elementos para los dos ColorBoxView puntos:
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();
}
···
}
Este programa usa la característica de posicionamiento y tamaño relativos de AbsoluteLayout . El ancho y alto de cada uno se establecen en valores fraccionales, concretamente el 85 % de 1 dividido por el número de puntos BoxView horizontales y verticales. Las posiciones también se establecen en valores fraccionales.
Dado que todas las posiciones y tamaños son relativos al tamaño total de , el controlador de la página AbsoluteLayout solo necesita establecer un de SizeChangedHeightRequestAbsoluteLayout :
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;
}
···
}
El ancho de AbsoluteLayout se establece automáticamente porque se ajusta al ancho completo de la página.
El código final de la clase procesa la devolución de llamada MainPage del temporizador y colore los puntos de cada dígito. La definición de las matrices multidimensionales al principio del archivo de código subyacente ayuda a que esta lógica sea la parte más sencilla del programa:
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;
}
}
}
Creación de un reloj analógico
Un reloj de matriz de puntos puede parecer una aplicación obvia de , pero los elementos también son capaces de BoxViewBoxView realizar un reloj análogo:
Todos los objetos visuales del programa BoxViewClock son elementos secundarios de . Estos elementos tienen un tamaño mediante LayoutBounds la propiedad adjunta y se giran mediante la propiedad Rotation .
Se crea una instancia de los tres elementos para las manos del reloj en el archivo XAML, pero no se BoxView coloca ni se dimensiona:
<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>
El constructor del archivo de código subyacente crea una instancia de los 60 elementos de las marcas de graduación alrededor de la BoxView circunferencia del reloj:
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);
}
···
}
El tamaño y el posicionamiento de todos los BoxView elementos se produce en el controlador para SizeChangedAbsoluteLayout . Una pequeña estructura interna de la clase denominada describe el tamaño de cada una de las tres manos con respecto al HandParams tamaño total del reloj:
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);
···
}
El controlador determina el centro y el radio de y, a continuación, tamaños y posiciones de SizeChangedAbsoluteLayout los 60 BoxView elementos usados como marcas de graduación. El for bucle finaliza estableciendo la propiedad de cada Rotation uno de estos BoxView elementos. Al final del controlador, se llama al método para cambiar el tamaño y colocar SizeChanged las tres manos del LayoutHand reloj:
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;
}
···
}
El LayoutHand método tamaño y coloca cada mano para apuntar directamente a la posición 12:00. Al final del método , la propiedad se establece en una posición correspondiente al AnchorY centro del reloj. Esto indica el centro de rotación.
Las manos se giran en la función de devolución de llamada del temporizador:
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;
}
}
La segunda mano se trata de forma un poco diferente: se aplica una función de aceleración de animación para que el movimiento parezca mecánico en lugar de suave. En cada paso, la segunda mano extrae un poco y, a continuación, sobresalte su destino. Este pequeño fragmento de código agrega mucho al realismo del movimiento.
Descarga del ejemplo


Game of
de