Пользовательские анимации в Xamarin.Forms
Класс анимации — это стандартный блок всех Xamarin.Forms анимаций, с методами расширения в классе ViewExtensions, создав один или несколько объектов анимации. В этой статье показано, как использовать класс анимации для создания и отмены анимаций, синхронизации нескольких анимаций и создания настраиваемых анимаций, которые не анимируются существующими методами анимации.
При создании Animation
объекта необходимо указать ряд параметров, включая начальные и конечные значения анимированного свойства, а также обратный вызов, который изменяет значение свойства. Объект Animation
также может поддерживать коллекцию дочерних анимаций, которые можно запускать и синхронизировать. Дополнительные сведения см. в разделе "Дочерние анимации".
Выполнение анимации, созданной с Animation
помощью класса, которое может включать или не включать дочерние анимации, достигается путем вызова Commit
метода. Этот метод задает длительность анимации и среди других элементов, обратный вызов, который определяет, следует ли повторять анимацию.
Кроме того, Animation
класс имеет IsEnabled
свойство, которое можно проверить, чтобы определить, отключены ли анимации операционной системой, например при активации режима экономии питания.
Создание анимации
При создании Animation
объекта, как правило, требуется не менее трех параметров, как показано в следующем примере кода:
var animation = new Animation (v => image.Scale = v, 1, 2);
Этот код определяет анимацию Scale
свойства экземпляра Image
из значения 1 до значения 2. Анимированное значение, которое является производным Xamarin.Forms, передается обратному вызову, указанному в качестве первого аргумента, где оно используется для изменения значения Scale
свойства.
Анимация запускается с вызовом Commit
метода, как показано в следующем примере кода:
animation.Commit (this, "SimpleAnimation", 16, 2000, Easing.Linear, (v, c) => image.Scale = 1, () => true);
Обратите внимание, что Commit
метод не возвращает Task
объект. Вместо этого уведомления предоставляются с помощью методов обратного вызова.
В методе указаны Commit
следующие аргументы:
- Первый аргумент (владелец) определяет владельца анимации. Это может быть визуальный элемент, на котором применяется анимация, или другой визуальный элемент, например страница.
- Второй аргумент (имя) определяет анимацию с именем. Имя объединяется с владельцем для уникальной идентификации анимации. Затем эту уникальную идентификацию можно использовать для определения того, выполняется ли анимация (
AnimationIsRunning
) или отменяет ее (AbortAnimation
). - Третий аргумент (частота) указывает количество миллисекунда между каждым вызовом метода обратного вызова, определенного в конструкторе
Animation
. - Четвертый аргумент (длина) указывает длительность анимации в миллисекундах.
- Пятый аргумент (упрощение) определяет функцию упрощения, которая будет использоваться в анимации. Кроме того, функцию упрощения можно указать в качестве аргумента конструктора
Animation
. Дополнительные сведения о функциях упрощения см. в разделе "Функции упрощения". - Шестой аргумент (готово) — это обратный вызов, который будет выполнен после завершения анимации. Этот обратный вызов принимает два аргумента, с первым аргументом, указывающим окончательное значение, и второй аргумент является
bool
заданнымtrue
, если анимация была отменена. Кроме того, готовый обратный вызов можно указать в качестве аргумента конструктораAnimation
. Однако с одной анимацией, если завершенные обратные вызовы указываются как в конструкторе, такAnimation
и в методеCommit
, будет выполняться только обратный вызов, указанный вCommit
методе. - Седьмой аргумент (повтор) — это обратный вызов, позволяющий повторять анимацию. Он вызывается в конце анимации и возвращает
true
значение, указывающее, что анимация должна повторяться.
Общий эффект заключается в создании анимации, которая увеличивает Scale
свойство Image
от 1 до 2 секунд (2000 миллисекунд), используя Linear
функцию упрощения. При каждом завершении анимации его Scale
свойство сбрасывается до 1, а анимация повторяется.
Примечание.
Одновременные анимации, которые выполняются независимо друг от друга, можно создать путем создания Animation
объекта для каждой анимации, а затем вызова метода для каждой Commit
анимации.
Дочерние анимации
Класс Animation
также поддерживает дочерние анимации, которые включают создание Animation
объекта, в который добавляются другие Animation
объекты. Это позволяет выполнять и синхронизировать ряд анимаций. В следующем примере кода показано создание и выполнение дочерних анимаций:
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));
Кроме того, пример кода можно писать более кратко, как показано в следующем примере кода:
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));
В обоих примерах кода создается родительский Animation
объект, к которому добавляются дополнительные Animation
объекты. Первые два аргумента Add
метода указывают время начала и завершения дочерней анимации. Значения аргументов должны быть от 0 до 1 и представлять относительный период в родительской анимации, которая будет активной указанной дочерней анимацией. Таким образом, в этом примере scaleUpAnimation
будет активно в течение первой половины анимации, scaleDownAnimation
он будет активным во второй половине анимации, и rotateAnimation
будет активным в течение всей длительности.
Общий эффект заключается в том, что анимация происходит в течение 4 секунд (4000 миллисекунд). Анимирует scaleUpAnimation
Scale
свойство от 1 до 2 секунд. Затем scaleDownAnimation
анимирует Scale
свойство от 2 до 1, более 2 секунд. Хотя оба анимации масштабирования происходят, rotateAnimation
свойство анимирует Rotation
от 0 до 360, более 4 секунд. Обратите внимание, что анимации масштабирования также используют функции упрощения. Функция SpringIn
упрощения приводит к первоначальному Image
сокращению перед получением большего размера, а SpringOut
функция упрощения приводит Image
к тому, что функция становится меньше фактического размера в конце полной анимации.
Существует ряд различий Animation
между объектом, использующим дочерние анимации, и одним из них не является:
- При использовании дочерних анимаций завершенный обратный вызов в дочерней анимации указывает, когда ребенок завершился, и завершенный обратный вызов, переданный
Commit
методу, указывает на завершение всей анимации. - При использовании дочерних анимаций возврат
true
из обратного вызоваCommit
повторения метода не приведет к повтору анимации, но анимация будет продолжать выполняться без новых значений. - При включении функции облегчения в
Commit
методе и функция упрощения возвращает значение больше 1, анимация будет завершена. Если функция облегчения возвращает значение меньше 0, значение зажато до 0. Чтобы использовать функцию упрощения, возвращающую значение меньше 0 или больше 1, он должен указываться в одной из дочерних анимаций, а не в методеCommit
.
Класс Animation
также включает WithConcurrent
методы, которые можно использовать для добавления дочерних анимаций в родительский Animation
объект. Однако их значения аргументов начала и завершения не ограничены 0 до 1, но только эта часть дочерней анимации, которая соответствует диапазону от 0 до 1, будет активной. Например, если WithConcurrent
вызов метода определяет дочернюю анимацию, предназначенную Scale
для свойства от 1 до 6, но с начальными и конечными значениями –2 и 3, начальное значение -2 соответствует Scale
значению 1, а готовое значение 3 соответствует Scale
значению 6. Поскольку значения за пределами диапазона 0 и 1 не играют никакой роли в анимации, Scale
свойство будет анимировано только от 3 до 6.
Отмена анимации
Приложение может отменить анимацию с вызовом AbortAnimation
метода расширения, как показано в следующем примере кода:
this.AbortAnimation ("SimpleAnimation");
Обратите внимание, что анимации однозначно определяются сочетанием владельца анимации и имени анимации. Поэтому для отмены анимации необходимо указать владельца и имя, указанное при запуске анимации. Поэтому пример кода немедленно отменит анимацию с именем SimpleAnimation
, принадлежащей странице.
Создание пользовательской анимации
В примерах, показанных здесь до сих пор, показаны анимации, которые могут быть достигнуты с помощью методов в ViewExtensions
классе. Однако преимущество Animation
класса заключается в том, что он имеет доступ к методу обратного вызова, который выполняется при изменении анимированного значения. Это позволяет обратному вызову реализовать любую нужную анимацию. Например, следующий пример кода анимирует BackgroundColor
свойство страницы, задав его Color
значениям, созданным Color.FromHsla
методом, со значениями от 0 до 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);
Результирующая анимация обеспечивает внешний вид фона страницы через цвета радуги.
Дополнительные примеры создания сложных анимаций, включая анимацию кривой Bezier, см. в главе 22 создания мобильных приложений с Xamarin.Formsпомощью.
Создание пользовательского метода расширения анимации
Методы расширения в ViewExtensions
классе анимирует свойство из текущего значения в указанное значение. Это затрудняет создание, например, ColorTo
метода анимации, который можно использовать для анимации цвета от одного значения к другому, так как:
- Единственное
Color
свойство, определенноеVisualElement
классом, —BackgroundColor
это не всегда нужноеColor
свойство для анимации. - Часто текущее значение
Color
свойства , которое не являетсяColor.Default
реальным цветом, и которое нельзя использовать в вычислениях интерполяции.
Решение этой проблемы заключается в том, чтобы метод не ColorTo
предназначен для определенного Color
свойства. Вместо этого его можно записать с помощью метода обратного вызова, который передает интерполированное Color
значение обратно вызывающему объекту. Кроме того, метод будет принимать аргументы start и end Color
.
Этот ColorTo
метод можно реализовать как метод расширения, который использует Animate
метод в AnimationExtensions
классе для предоставления его функциональных возможностей. Это связано с тем, что Animate
метод можно использовать для целевых свойств, которые не являются типом double
, как показано в следующем примере кода:
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;
}
}
Для Animate
метода требуется аргумент преобразования , который является методом обратного вызова. Входные данные для этого обратного вызова всегда double
варьируются от 0 до 1. ColorTo
Поэтому метод определяет свое собственное преобразованиеFunc
, которое принимает double
диапазон от 0 до 1, и возвращает Color
значение, соответствующее этому значению. Значение Color
вычисляется путем интерполяции R
аргументов , G
B
и A
значений двух указанных Color
аргументов. Затем Color
значение передается методу обратного вызова для приложения в определенное свойство.
Этот подход позволяет ColorTo
методу анимировать любое Color
свойство, как показано в следующем примере кода:
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);
В этом примере ColorTo
кода метод анимирует TextColor
свойства страницы и BackgroundColor
Color
свойстваLabel
BackgroundColor
.BoxView