Поделиться через


Пользовательские анимации в 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аргументов , GBи 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 свойстваLabelBackgroundColor.BoxView