Resumo do Capítulo 26. Layouts personalizados
Observação
Este livro foi publicado na primavera de 2016, e não foi atualizado desde então. Há muito no livro que permanece valioso, mas parte do material está desatualizado, e alguns tópicos não estão mais totalmente corretos ou completos.
Xamarin.Forms inclui várias classes derivadas de Layout<View>
:
StackLayout
,Grid
,AbsoluteLayout
eRelativeLayout
.
Este capítulo descreve como criar suas próprias classes derivadas do Layout<View>
.
Uma visão geral do layout
Não há um sistema centralizado que lide com o Xamarin.Forms layout. Cada elemento é responsável por determinar qual deve ser seu próprio tamanho e como se renderizar dentro de uma determinada área.
Pais e filhos
Todo elemento que tem filhos é responsável por posicioná-las dentro de si. É o pai que, em última análise, determina o tamanho que seus filhos devem ter com base no tamanho que ele tem disponível e no tamanho que a criança quer ser.
Dimensionamento e posicionamento
O layout começa na parte superior da árvore visual com a página e, em seguida, prossegue por todos os ramos. O método público mais importante no layout é Layout
definido por VisualElement
. Cada elemento que é pai de outros elementos exige Layout
que cada um de seus filhos dê à criança um tamanho e uma posição em relação a si mesma na forma de um Rectangle
valor. Essas Layout
chamadas se propagam pela árvore visual.
Uma chamada para Layout
é necessária para que um elemento apareça na tela e faz com que as seguintes propriedades somente leitura sejam definidas. Eles são consistentes com o passado para o Rectangle
método:
Bounds
do tipoRectangle
X
do tipodouble
Y
do tipodouble
Width
do tipodouble
Height
do tipodouble
Antes da Layout
chamada, Height
e Width
ter valores simulados de –1.
Uma chamada para Layout
também dispara chamadas para os seguintes métodos protegidos:
SizeAllocated
, que chamaOnSizeAllocated
, que pode ser substituído.
Finalmente, o seguinte evento é disparado:
O OnSizeAllocated
método é substituído por Page
e Layout
, que são as duas únicas classes em Xamarin.Forms que podem ter filhos. As chamadas de método substituídas
UpdateChildrenLayout
paraPage
derivativos eUpdateChildrenLayout
paraLayout
derivativos, que chamaLayoutChildren
paraPage
derivativos eLayoutChildren
derivadosLayout
.
LayoutChildren
em seguida, chama Layout
por cada um dos filhos do elemento. Se pelo menos uma criança tiver uma nova Bounds
configuração, o seguinte evento será acionado:
LayoutChanged
paraPage
derivados eLayoutChanged
paraLayout
derivados
Restrições e solicitações de tamanho
Pois LayoutChildren
para chamar Layout
inteligentemente todos os seus filhos, ele deve saber um tamanho preferido ou desejado para as crianças. Portanto, os chamados para Layout
cada uma das crianças são geralmente precedidos de chamadas para
Depois que o livro foi publicado, o GetSizeRequest
método foi preterido e substituído por
O Measure
método acomoda a Margin
propriedade e inclui um argumento do tipo MeasureFlag
, que tem dois membros:
IncludeMargins
None
não incluir margens
Para muitos elementos, GetSizeRequest
ou Measure
obtém o tamanho nativo do elemento de seu renderizador. Ambos os métodos têm parâmetros para restrições de largura e altura. Por exemplo, a Label
usará a restrição de largura para determinar como quebrar várias linhas de texto.
Ambos GetSizeRequest
e Measure
retornam um valor do tipo SizeRequest
, que tem duas propriedades:
Muitas vezes, esses dois valores são os mesmos, e o Minimum
valor geralmente pode ser ignorado.
VisualElement
também define um método protegido semelhante ao GetSizeRequest
que é chamado de GetSizeRequest
:
OnSizeRequest
retorna umSizeRequest
valor
Esse método agora foi preterido e substituído por:
Toda classe que deriva de Layout
ou Layout<T>
deve substituir OnSizeRequest
ou OnMeasure
. É aqui que uma classe de layout determina seu próprio tamanho, que geralmente é baseado no tamanho de seus filhos, que ela obtém chamando GetSizeRequest
ou Measure
sobre os filhos. Antes e depois de chamar OnSizeRequest
ou OnMeasure
, GetSizeRequest
ou Measure
faz ajustes com base nas seguintes propriedades:
WidthRequest
do tipodouble
, afeta aRequest
propriedade deSizeRequest
HeightRequest
do tipodouble
, afeta aRequest
propriedade deSizeRequest
MinimumWidthRequest
do tipodouble
, afeta aMinimum
propriedade deSizeRequest
MinimumHeightRequest
do tipodouble
, afeta aMinimum
propriedade deSizeRequest
Restrições infinitas
Os argumentos de restrição passados para GetSizeRequest
(ou Measure
) e OnSizeRequest
(ou OnMeasure
) podem ser infinitos (ou seja, valores de Double.PositiveInfinity
). No entanto, o SizeRequest
retornado desses métodos não pode conter dimensões infinitas.
Restrições infinitas indicam que o tamanho solicitado deve refletir o tamanho natural do elemento. Uma vertical StackLayout
chama GetSizeRequest
(ou Measure
) seus filhos com uma restrição de altura infinita. Um layout de pilha horizontal chama GetSizeRequest
(ou Measure
) em seus filhos com uma restrição de largura infinita. Um AbsoluteLayout
chama GetSizeRequest
(ou Measure
) em seus filhos com restrições infinitas de largura e altura.
Espreitando dentro do processo
O ExploreChildSize exibe informações de solicitação de restrição e tamanho para um layout simples.
Derivando do Modo de Exibição de Layout<>
Uma classe de layout personalizada deriva de Layout<View>
. Tem duas responsabilidades:
- Substitua
OnMeasure
para chamarMeasure
todos os filhos do layout. Retornar um tamanho solicitado para o layout em si - Substituir
LayoutChildren
para chamarLayout
todos os filhos do layout
O for
loop ou foreach
nessas substituições deve ignorar qualquer filho cuja IsVisible
propriedade esteja definida como false
.
Uma chamada para OnMeasure
não é garantida. OnMeasure
não será chamado se o pai do layout estiver governando o tamanho do layout (por exemplo, um layout que preenche uma página). Por esse motivo, LayoutChildren
não pode contar com tamanhos de criança obtidos durante a OnMeasure
chamada. Muitas vezes, LayoutChildren
deve chamar Measure
os filhos do layout, ou você pode implementar algum tipo de lógica de cache de tamanho (a ser discutido mais tarde).
Um exemplo fácil
O exemplo VerticalStackDemo contém uma classe simplificada VerticalStack
e uma demonstração de seu uso.
Posicionamento vertical e horizontal simplificado
Um dos trabalhos que VerticalStack
devem ser executados ocorre durante a LayoutChildren
substituição. O método usa a propriedade do filho para determinar como posicioná-lo dentro de HorizontalOptions
seu slot no VerticalStack
. Em vez disso, você pode chamar o método Layout.LayoutChildIntoBoundingRect
estático . Esse método chama Measure
o filho e usa suas HorizontalOptions
propriedades e VerticalOptions
para posicioná-lo dentro do retângulo especificado.
Invalidação
Muitas vezes, uma alteração na propriedade de um elemento afeta como esse elemento aparece no layout. O layout deve ser invalidado para acionar um novo layout.
VisualElement
define um método InvalidateMeasure
protegido , que geralmente é chamado pelo manipulador de propriedade alterada de qualquer propriedade vinculável cuja alteração afeta o tamanho do elemento. O InvalidateMeasure
método dispara um MeasureInvalidated
evento.
A Layout
classe define um método protegido semelhante chamado InvalidateLayout
, que uma Layout
derivada deve chamar para qualquer alteração que afete como ela posiciona e dimensiona seus filhos.
Algumas regras para codificação de layouts
As propriedades definidas por
Layout<T>
derivativos devem ser apoiadas por propriedades vinculáveis, e os manipuladores alterados por propriedade devem chamarInvalidateLayout
.Um
Layout<T>
derivado que define propriedades vinculáveis anexadas deve substituirOnAdded
para adicionar um manipulador de propriedade alterada a seus filhos eOnRemoved
remover esse manipulador. O manipulador deve verificar se há alterações nessas propriedades vinculáveis anexadas e responder chamandoInvalidateLayout
.Um
Layout<T>
derivado que implementa um cache de tamanhos filho deve substituirInvalidateLayout
eOnChildMeasureInvalidated
limpar o cache quando esses métodos são chamados.
Um layout com propriedades
A WrapLayout
classe no Xamarin.FormsBook.Toolkit assume que todos os seus filhos são do mesmo tamanho e envolve os filhos de uma linha (ou coluna) para a próxima. Ele define uma Orientation
propriedade como StackLayout
, e ColumnSpacing
RowSpacing
e propriedades como Grid
, e armazena em cache tamanhos filho.
A amostra PhotoWrap coloca um WrapLayout
em um ScrollView
para exibir fotos de estoque.
Não são permitidas dimensões irrestritas!
O UniformGridLayout
na Xamarin.Formsbiblioteca Book.Toolkit destina-se a exibir todos os seus filhos dentro de si mesmo. Portanto, não pode lidar com dimensões irrestritas e abre uma exceção se for encontrada.
O exemplo PhotoGrid demonstra UniformGridLayout
:
Crianças sobrepostas
Um Layout<T>
derivado pode se sobrepor a seus filhos. No entanto, as crianças são renderizadas em sua ordem na Children
coleção, e não na ordem em que seus Layout
métodos são chamados.
A Layout
classe define dois métodos que permitem mover um filho dentro da coleção:
LowerChild
Para mover uma criança para o início da coleçãoRaiseChild
Para mover uma criança para o final da coleção
Para crianças sobrepostas, as crianças no final da coleção aparecem visualmente em cima das crianças no início da coleção.
A OverlapLayout
classe na Xamarin.Formsbiblioteca Book.Toolkit define uma propriedade anexada para indicar a ordem de renderização e, assim, permitir que um de seus filhos seja exibido sobre os outros. O exemplo StudentCardFile demonstra isso:
Mais propriedades vinculáveis anexadas
A CartesianLayout
classe na Xamarin.Formsbiblioteca Book.Toolkit define propriedades vinculáveis anexadas para especificar dois Point
valores e um valor de espessura e manipula BoxView
elementos para se assemelhar a linhas.
O exemplo UnitCube usa isso para desenhar um cubo 3D.
Layout e LayoutTo
Um Layout<T>
derivado pode chamar LayoutTo
em vez de Layout
animar o layout. A AnimatedCartesianLayout
classe faz isso, e o exemplo AnimatedUnitCube demonstra isso.