Resumen del capítulo 26. Diseños personalizados
Nota:
Este libro se publicó en la primavera de 2016 y no se ha actualizado desde entonces. Gran parte del libro sigue siendo útil, pero algunos de los materiales están anticuados y algunos temas ya no son completamente correctos o completos.
Xamarin.Forms incluye varias clases derivadas de Layout<View>
:
StackLayout
,Grid
,AbsoluteLayout
yRelativeLayout
.
En este capítulo se describe cómo crear sus propias clases que se derivan de Layout<View>
.
Información general sobre el diseño
No hay ningún sistema centralizado que controle el diseño de Xamarin.Forms. Cada elemento es responsable de determinar cuál debe ser su propio tamaño y cómo representarse en un área determinada.
Elementos primarios y secundarios
Cada elemento que tenga elementos secundarios es responsable de colocar esos elementos secundarios en su interior. Es el elemento primario el que determina en última instancia el tamaño de sus elementos secundarios en función del tamaño que tiene disponible y el tamaño que el elemento secundario desea tener.
Ajuste del tamaño y el posicionamiento
El diseño comienza en la parte superior del árbol visual con la página y luego continúa a través de todas las ramas. El método público más importante en el diseño es Layout
definido por VisualElement
. Cada elemento que tiene un rango superior respecto a otros elementos llama a Layout
para cada uno de sus elementos secundarios para darle a estos un tamaño y una posición con relación a sí mismo en forma de un valor Rectangle
. Estas llamadas a Layout
se propagan a través del árbol visual.
Se requiere una llamada a Layout
para que un elemento aparezca en la pantalla, y hace que se establezcan las siguientes propiedades de solo lectura. Son coherentes con los Rectangle
pasados al método:
Bounds
de tipoRectangle
X
de tipodouble
Y
de tipodouble
Width
de tipodouble
Height
de tipodouble
Antes de la llamada a Layout
, Height
y Width
tienen valores ficticios de –1.
Una llamada a Layout
también desencadena llamadas a los siguientes métodos protegidos:
SizeAllocated
, que llama.OnSizeAllocated
, que puede reemplazarse.
Por último, se desencadena el siguiente evento:
El método OnSizeAllocated
se reemplaza por Page
y Layout
, que son las dos únicas clases de Xamarin.Forms que pueden tener elementos secundarios. El método reemplazado llama a:
UpdateChildrenLayout
para los derivados dePage
yUpdateChildrenLayout
para los derivados deLayout
, que llama.LayoutChildren
para los derivados dePage
yLayoutChildren
para los derivados deLayout
.
LayoutChildren
a continuación, llama a Layout
para cada uno de los elementos secundarios del elemento. Si al menos un elemento secundario tiene un nuevo valor de Bounds
, se desencadena el siguiente evento:
LayoutChanged
para los derivados dePage
yLayoutChanged
para los derivados deLayout
.
Restricciones y solicitudes de tamaño
Para que LayoutChildren
llame de forma inteligente a Layout
en todos sus elementos secundarios, debe conocer un tamaño preferido o deseado para los elementos secundarios. Por lo tanto, las llamadas a Layout
para cada uno de los elementos secundarios están precedidas por llamadas a
Una vez publicado el libro, el método de GetSizeRequest
ha quedado en desuso y se ha reemplazado por
El método Measure
aloja la propiedad Margin
e incluye un argumento de tipo MeasureFlag
, que tiene dos miembros:
IncludeMargins
None
para no incluir márgenes
Para muchos elementos, GetSizeRequest
o Measure
obtienen el tamaño nativo del elemento de su representador. Ambos métodos tienen parámetros para las restricciones de ancho y alto. Por ejemplo, Label
utilizará la restricción de ancho para determinar cómo se ajustan varias líneas de texto.
Tanto GetSizeRequest
como Measure
devuelven un valor de tipo SizeRequest
, que tiene dos propiedades:
Con frecuencia, estos dos valores son el mismo y, por lo general, el valor Minimum
se puede pasar por alto.
VisualElement
también define un método protegido similar a GetSizeRequest
al que se llama desde GetSizeRequest
:
OnSizeRequest
devuelve un valorSizeRequest
.
Ese método ahora está en desuso y se reemplaza por:
Cada clase que se deriva de Layout
o Layout<T>
debe invalidar OnSizeRequest
o OnMeasure
. Aquí es donde una clase de diseño determina su propio tamaño, que generalmente se basa en el tamaño de sus elementos secundarios, que obtiene llamando a GetSizeRequest
o Measure
en los elementos secundarios. Antes y después de llamar a OnSizeRequest
o OnMeasure
, GetSizeRequest
o Measure
realiza ajustes en función de las siguientes propiedades:
WidthRequest
de tipodouble
, afecta a la propiedadRequest
deSizeRequest
.HeightRequest
de tipodouble
, afecta a la propiedadRequest
deSizeRequest
.MinimumWidthRequest
de tipodouble
, afecta a la propiedadMinimum
deSizeRequest
.MinimumHeightRequest
de tipodouble
, afecta a la propiedadMinimum
deSizeRequest
.
Restricciones infinitas
Los argumentos de restricción pasados a GetSizeRequest
(o Measure
) y OnSizeRequest
(o OnMeasure
) pueden ser infinitos (es decir, valores de Double.PositiveInfinity
). Sin embargo, los SizeRequest
devueltos de estos métodos no pueden contener dimensiones infinitas.
Las restricciones infinitas indican que el tamaño solicitado debe reflejar el tamaño natural del elemento. Un StackLayout
vertical llama a GetSizeRequest
(o Measure
) en sus elementos secundarios con una restricción de alto infinito. Un diseño de pila horizontal llama a GetSizeRequest
(o Measure
) en sus elementos secundarios con una restricción de ancho infinito. Un AbsoluteLayout
llama a GetSizeRequest
(o Measure
) en sus elementos secundarios con restricciones de ancho y alto infinitos.
Inspección dentro del proceso
El elemento ExploreChildSize muestra la información de la solicitud de restricción y tamaño para un diseño sencillo.
Derivación de Diseño<Vista>
Una clase de diseño personalizado se deriva de Layout<View>
. Tiene dos responsabilidades:
- Invalidar
OnMeasure
para llamar aMeasure
en todos los elementos secundarios del diseño. Devolver un tamaño solicitado para el propio diseño. - Invalidar
LayoutChildren
para llamar aLayout
en todos los elementos secundarios del diseño.
Los bucles for
o foreach
de estas invalidaciones deberían omitir cualquier elemento secundario cuya propiedad IsVisible
esté establecida en false
.
No se garantiza una llamada a OnMeasure
. No se llamará a OnMeasure
si el elemento primario del diseño controla el tamaño del diseño (por ejemplo, un diseño que llene una página). Por esta razón, LayoutChildren
no puede basarse en los tamaños secundarios obtenidos durante la llamada a OnMeasure
. Con mucha frecuencia, LayoutChildren
debe llamar a Measure
en los elementos secundarios del diseño, o puede implementar algún tipo de lógica de almacenamiento en caché del tamaño (que se tratará más adelante).
Un ejemplo sencillo
El ejemplo VerticalStackDemo contiene una clase VerticalStack
simplificada y una demostración de su uso.
Posicionamiento vertical y horizontal simplificado
Uno de los trabajos que VerticalStack
debe realizar se produce durante la invalidación de LayoutChildren
. El método usa la propiedad HorizontalOptions
del elemento secundario para determinar cómo colocar el elemento secundario dentro de su espacio en VerticalStack
. En su lugar, puede llamar al método estático Layout.LayoutChildIntoBoundingRect
. Este método llama a Measure
en el elemento secundario y utiliza sus propiedades HorizontalOptions
y VerticalOptions
para colocar el elemento secundario dentro del rectángulo especificado.
Invalidación
A menudo, un cambio en la propiedad de un elemento afecta al modo en que ese elemento aparece en el diseño. El diseño se debe invalidar para desencadenar un nuevo diseño.
VisualElement
define un método protegido InvalidateMeasure
, al que se suele llamar mediante el controlador modificado por la propiedad de cualquier propiedad enlazable cuyo cambio afecte al tamaño del elemento. El método InvalidateMeasure
activa un evento MeasureInvalidated
.
La clase Layout
define un método protegido similar denominado InvalidateLayout
, al que un derivado de Layout
debe llamar para cualquier cambio que afecte al modo en que coloca y ajusta el tamaño de sus elementos secundarios.
Algunas reglas para la codificación de diseños
Las propiedades definidas por derivados de
Layout<T>
deben estar respaldadas por propiedades enlazables y los controladores modificados por propiedad deben llamar aInvalidateLayout
.Un derivado de
Layout<T>
que define las propiedades enlazables asociadas debe reemplazar aOnAdded
para agregar un controlador modificado por propiedad a sus elementos secundarios yOnRemoved
para quitar ese controlador. El controlador debe comprobar si hay cambios en estas propiedades enlazables asociadas y responder llamando aInvalidateLayout
.Un derivado de
Layout<T>
que implementa una memoria caché de tamaños secundarios debe reemplazar aInvalidateLayout
yOnChildMeasureInvalidated
y borrar la memoria caché cuando se llama a estos métodos.
Un diseño con propiedades
En la clase WrapLayout
de Xamarin.FormsBook.Toolkit se supone que todos sus elementos secundarios tienen el mismo tamaño, y se ajustan los elementos secundarios de una fila (o columna) a la siguiente. Define una propiedad Orientation
como StackLayout
, y las propiedades ColumnSpacing
y RowSpacing
como Grid
, y almacena en caché los tamaños secundarios.
En el ejemplo de PhotoWrap se coloca un WrapLayout
en un ScrollView
para mostrar las fotos en existencias.
No se permiten dimensiones sin restricciones.
El valor UniformGridLayout
de la biblioteca Xamarin.FormsBook.Toolkit está diseñada para mostrar todos sus elementos secundarios en su interior. Por lo tanto, no puede tratar con dimensiones sin restricciones y genera una excepción si se encuentra una.
En el ejemplo PhotoGrid se muestra UniformGridLayout
:
Elementos secundarios superpuestos
Un derivado de Layout<T>
puede superponerse a sus elementos secundarios. Sin embargo, los elementos secundarios se representan en su orden en la colección de Children
y no en el orden en que se llama a los métodos de Layout
.
La clase Layout
define dos métodos que le permiten desplazar un elemento secundario dentro de la colección:
LowerChild
para desplazar un elemento secundario al principio de la colecciónRaiseChild
para desplazar un elemento secundario al final de la colección
En el caso de los elementos secundarios superpuestos, los elementos secundarios al final de la colección aparecen visualmente encima de los elementos secundarios al principio de la colección.
La clase OverlapLayout
de la biblioteca Xamarin.FormsBook.Toolkit define una propiedad asociada para indicar el orden de representación y, por tanto, permitir que uno de sus elementos secundarios se muestre encima de los demás. En el ejemplo StudentCardFile se muestra lo siguiente:
Más propiedades enlazables asociadas
La clase CartesianLayout
de la biblioteca Xamarin.FormsBook.Toolkit define las propiedades enlazables asociadas para especificar dos valores de Point
y un valor de grosor y manipula los elementos de BoxView
para que se parezca a las líneas.
El ejemplo UnitCube se usa para dibujar un cubo 3D.
Layout y LayoutTo
Un derivado de Layout<T>
puede llamar a LayoutTo
en lugar de Layout
para animar el diseño. La clase AnimatedCartesianLayout
se ocupa de ello, y el ejemplo AnimatedUnitCube lo demuestra.