Animações personalizadas em Xamarin.Forms
A classe Animation é o bloco de construção de todas as Xamarin.Forms animações, com os métodos de extensão na classe ViewExtensions criando um ou mais objetos Animation. Este artigo demonstra como usar a classe Animation para criar e cancelar animações, sincronizar várias animações e criar animações personalizadas que animam propriedades que não são animadas pelos métodos de animação existentes.
Vários parâmetros devem ser especificados ao criar um Animation
objeto, incluindo valores inicial e final da propriedade que está sendo animada e um retorno de chamada que altera o valor da propriedade. Um Animation
objeto também pode manter uma coleção de animações filho que podem ser executadas e sincronizadas. Para obter mais informações, consulte Animações filho.
A execução de uma animação criada com a Animation
classe, que pode ou não incluir animações filho, é obtida chamando o Commit
método. Esse método especifica a duração da animação e, entre outros itens, um retorno de chamada que controla se a animação deve ser repetida.
Além disso, a Animation
classe tem uma IsEnabled
propriedade que pode ser examinada para determinar se as animações foram desabilitadas pelo sistema operacional, como quando o modo de economia de energia é ativado.
Criar uma animação
Ao criar um Animation
objeto, normalmente, um mínimo de três parâmetros são necessários, conforme demonstrado no exemplo de código a seguir:
var animation = new Animation (v => image.Scale = v, 1, 2);
Esse código define uma animação da Scale
propriedade de uma Image
instância de um valor de 1 a um valor de 2. O valor animado, que é derivado por Xamarin.Forms, é passado para o retorno de chamada especificado como o primeiro argumento, onde é usado para alterar o Scale
valor da propriedade.
A animação é iniciada com uma chamada para o Commit
método, conforme demonstrado no exemplo de código a seguir:
animation.Commit (this, "SimpleAnimation", 16, 2000, Easing.Linear, (v, c) => image.Scale = 1, () => true);
Observe que o Commit
método não retorna um Task
objeto. Em vez disso, as notificações são fornecidas por meio de métodos de retorno de chamada.
Os seguintes argumentos são especificados no Commit
método:
- O primeiro argumento (proprietário) identifica o proprietário da animação. Pode ser o elemento visual no qual a animação é aplicada ou outro elemento visual, como a página.
- O segundo argumento (nome) identifica a animação com um nome. O nome é combinado com o proprietário para identificar exclusivamente a animação. Essa identificação exclusiva pode ser usada para determinar se a animação está sendo executada (
AnimationIsRunning
) ou para cancelá-la (AbortAnimation
). - O terceiro argumento (taxa) indica o número de milissegundos entre cada chamada para o método de retorno de chamada definido no
Animation
construtor. - O quarto argumento (comprimento) indica a duração da animação, em milissegundos.
- O quinto argumento (atenuação) define a função de atenuação a ser usada na animação. Como alternativa, a função de easing pode ser especificada como um argumento para o
Animation
construtor. Para obter mais informações sobre funções de atenuação, consulte Funções de atenuação. - O sexto argumento (finished) é um retorno de chamada que será executado quando a animação for concluída. Esse retorno de chamada usa dois argumentos, com o primeiro argumento indicando um valor final e o segundo argumento sendo um
bool
definido comotrue
se a animação fosse cancelada. Como alternativa, o retorno de chamada concluído pode ser especificado como um argumento para oAnimation
construtor. No entanto, com uma única animação, se os retornos de chamada concluídos forem especificados no construtor e noAnimation
Commit
método, somente o retorno de chamada especificado noCommit
método será executado. - O sétimo argumento (repetição) é um retorno de chamada que permite que a animação seja repetida. Ele é chamado no final da animação e retornar
true
indica que a animação deve ser repetida.
O efeito geral é criar uma animação que aumenta a Scale
propriedade de um Image
de 1 para 2, ao longo de 2 segundos (2000 milissegundos), usando a Linear
função de atenuação. Cada vez que a animação é concluída, sua Scale
propriedade é redefinida como 1 e a animação é repetida.
Observação
Animações simultâneas, que são executadas independentemente umas das outras, podem ser construídas criando um Animation
objeto para cada animação e, em seguida, chamando o Commit
método em cada animação.
Animações secundárias
A Animation
classe também dá suporte a animações filho, o que envolve a criação de um Animation
objeto ao qual outros Animation
objetos são adicionados. Isso permite que uma série de animações seja executada e sincronizada. O exemplo de código a seguir demonstra a criação e a execução de animações filho:
var parentAnimation = new Animation ();
var scaleUpAnimation = new Animation (v => image.Scale = v, 1, 2, Easing.SpringIn);
var rotateAnimation = new Animation (v => image.Rotation = v, 0, 360);
var scaleDownAnimation = new Animation (v => image.Scale = v, 2, 1, Easing.SpringOut);
parentAnimation.Add (0, 0.5, scaleUpAnimation);
parentAnimation.Add (0, 1, rotateAnimation);
parentAnimation.Add (0.5, 1, scaleDownAnimation);
parentAnimation.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));
Como alternativa, o exemplo de código pode ser escrito de forma mais concisa, conforme demonstrado no exemplo de código a seguir:
new Animation {
{ 0, 0.5, new Animation (v => image.Scale = v, 1, 2) },
{ 0, 1, new Animation (v => image.Rotation = v, 0, 360) },
{ 0.5, 1, new Animation (v => image.Scale = v, 2, 1) }
}.Commit (this, "ChildAnimations", 16, 4000, null, (v, c) => SetIsEnabledButtonState (true, false));
Em ambos os exemplos de código, um objeto pai Animation
é criado, ao qual objetos adicionais Animation
são adicionados. Os dois primeiros argumentos para o Add
método especificam quando iniciar e concluir a animação filho. Os valores do argumento devem estar entre 0 e 1 e representam o período relativo dentro da animação pai em que a animação secundária especificada estará ativa. Portanto, neste exemplo, o scaleUpAnimation
estará ativo para a primeira metade da animação, o scaleDownAnimation
estará ativo para a segunda metade da animação e o rotateAnimation
estará ativo por toda a duração.
O efeito geral é que a animação ocorre ao longo de 4 segundos (4000 milissegundos). O scaleUpAnimation
anima a Scale
propriedade de 1 a 2, ao longo de 2 segundos. Em scaleDownAnimation
seguida, anima a Scale
propriedade de 2 para 1, ao longo de 2 segundos. Enquanto ambas as animações de escala estão ocorrendo, o anima rotateAnimation
a Rotation
propriedade de 0 a 360, ao longo de 4 segundos. Observe que as animações de dimensionamento também usam funções de atenuação. A SpringIn
função de atenuação faz com que o Image
inicialmente encolha antes de ficar maior, e a SpringOut
função de atenuação faz com que o Image
se torne menor do que seu tamanho real no final da animação completa.
Há várias diferenças entre um Animation
objeto que usa animações filho e um que não usa:
- Ao usar animações filho, o retorno de chamada concluído em uma animação filho indica quando o filho foi concluído e o retorno de chamada concluído passado para o
Commit
método indica quando toda a animação foi concluída. - Ao usar animações filho, retornar
true
do retorno de chamada de repetição noCommit
método não fará com que a animação se repita, mas a animação continuará a ser executada sem novos valores. - Ao incluir uma função de atenuação no
Commit
método, e a função de atenuação retornar um valor maior que 1, a animação será encerrada. Se a função de easing retornar um valor menor que 0, o valor será fixado em 0. Para usar uma função de easing que retorna um valor menor que 0 ou maior que 1, ela deve ser especificada em uma das animações filho, em vez de noCommit
método.
A Animation
classe também inclui WithConcurrent
métodos que podem ser usados para adicionar animações filho a um objeto pai Animation
. No entanto, seus valores de argumento de início e término não são restritos a 0 a 1, mas somente a parte da animação filho que corresponde a um intervalo de 0 a 1 estará ativa. Por exemplo, se uma WithConcurrent
chamada de método definir uma animação filho direcionada a uma Scale
propriedade de 1 a 6, mas com valores de início e término de -2 e 3, o valor inicial de -2 corresponderá a um Scale
valor de 1 e o valor final de 3 corresponderá a um Scale
valor de 6. Como os valores fora do intervalo de 0 e 1 não desempenham nenhum papel em uma animação, a Scale
propriedade só será animada de 3 a 6.
Cancelar uma animação
Um aplicativo pode cancelar uma animação com uma chamada para o AbortAnimation
método de extensão, conforme demonstrado no exemplo de código a seguir:
this.AbortAnimation ("SimpleAnimation");
Observe que as animações são identificadas exclusivamente por uma combinação do proprietário da animação e do nome da animação. Portanto, o proprietário e o nome especificados ao executar a animação devem ser especificados para cancelar a animação. Portanto, o exemplo de código cancelará imediatamente a animação nomeada SimpleAnimation
que pertence à página.
Criar uma animação personalizada
Os exemplos mostrados aqui até agora demonstraram animações que poderiam ser igualmente alcançadas com os métodos da ViewExtensions
classe. No entanto, a Animation
vantagem da classe é que ela tem acesso ao método de retorno de chamada, que é executado quando o valor animado muda. Isso permite que o retorno de chamada implemente qualquer animação desejada. Por exemplo, o exemplo de código a seguir anima a BackgroundColor
propriedade de uma página definindo-a Color.FromHsla
como Color
valores criados pelo método, com valores de matiz variando de 0 a 1:
new Animation (callback: v => BackgroundColor = Color.FromHsla (v, 1, 0.5),
start: 0,
end: 1).Commit (this, "Animation", 16, 4000, Easing.Linear, (v, c) => BackgroundColor = Color.Default);
A animação resultante fornece a aparência de avançar o plano de fundo da página através das cores do arco-íris.
Para obter mais exemplos de criação de animações complexas, incluindo uma animação de curva de Bézier, consulte o Capítulo 22 de Criação de aplicativos móveis com Xamarin.Formso .
Criar um método de extensão de animação personalizado
Os métodos de extensão na ViewExtensions
classe animam uma propriedade de seu valor atual para um valor especificado. Isso dificulta a criação, por exemplo, de um ColorTo
método de animação que pode ser usado para animar uma cor de um valor para outro, porque:
- A única
Color
propriedade definida pelaVisualElement
classe éBackgroundColor
, que nem sempre é a propriedade desejadaColor
para animar. - Muitas vezes, o valor atual de uma
Color
propriedade éColor.Default
, que não é uma cor real e que não pode ser usada em cálculos de interpolação.
A solução para esse problema é não fazer com que o ColorTo
método tenha como alvo uma propriedade específica Color
. Em vez disso, ele pode ser gravado com um método de retorno de chamada que passa o valor interpolado Color
de volta para o chamador. Além disso, o método receberá argumentos de início e fim Color
.
O ColorTo
método pode ser implementado como um método de extensão que usa o Animate
AnimationExtensions
método na classe para fornecer sua funcionalidade. Isso ocorre porque o Animate
método pode ser usado para direcionar propriedades que não são do tipo double
, conforme demonstrado no exemplo de código a seguir:
public static class ViewExtensions
{
public static Task<bool> ColorTo(this VisualElement self, Color fromColor, Color toColor, Action<Color> callback, uint length = 250, Easing easing = null)
{
Func<double, Color> transform = (t) =>
Color.FromRgba(fromColor.R + t * (toColor.R - fromColor.R),
fromColor.G + t * (toColor.G - fromColor.G),
fromColor.B + t * (toColor.B - fromColor.B),
fromColor.A + t * (toColor.A - fromColor.A));
return ColorAnimation(self, "ColorTo", transform, callback, length, easing);
}
public static void CancelAnimation(this VisualElement self)
{
self.AbortAnimation("ColorTo");
}
static Task<bool> ColorAnimation(VisualElement element, string name, Func<double, Color> transform, Action<Color> callback, uint length, Easing easing)
{
easing = easing ?? Easing.Linear;
var taskCompletionSource = new TaskCompletionSource<bool>();
element.Animate<Color>(name, transform, callback, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
return taskCompletionSource.Task;
}
}
O Animate
método requer um argumento transform, que é um método de retorno de chamada. A entrada para esse retorno de chamada é sempre um double
intervalo de 0 a 1. Portanto, o ColorTo
método define sua própria transformação Func
que aceita um double
intervalo de 0 a 1 e que retorna um Color
valor correspondente a esse valor. O Color
valor é calculado interpolando os R
valores , G
, B
, e A
dos dois argumentos fornecidos Color
. O Color
valor é então passado para o método de retorno de chamada para aplicação a uma propriedade específica.
Essa abordagem permite que o ColorTo
método anime qualquer Color
propriedade, conforme demonstrado no exemplo de código a seguir:
await Task.WhenAll(
label.ColorTo(Color.Red, Color.Blue, c => label.TextColor = c, 5000),
label.ColorTo(Color.Blue, Color.Red, c => label.BackgroundColor = c, 5000));
await this.ColorTo(Color.FromRgb(0, 0, 0), Color.FromRgb(255, 255, 255), c => BackgroundColor = c, 5000);
await boxView.ColorTo(Color.Blue, Color.Red, c => boxView.Color = c, 4000);
Neste exemplo de código, o ColorTo
método anima as TextColor
propriedades and BackgroundColor
de um Label
, a BackgroundColor
propriedade de uma página e a Color
propriedade de um BoxView
.