Анимация Xamarin.Forms при нажатии кнопки (флэш-фон)

Я хочу реализовать dialpad на моей форме. Теперь, в моем XAML, я тестирую кнопку: XAML

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
         x:Class="MyXFDemo001.Views.NewItemPage"
         Title="New Item">
<ContentPage.ToolbarItems>
    <ToolbarItem Text="Save" Clicked="Save_Clicked" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
    <StackLayout Spacing="20" Padding="15">
        <Button
            x:Name="buttonSelectContact"
            Clicked="buttonSelectContact_Clicked"
            Text="CLICK" BackgroundColor="#0080ff" />
    </StackLayout>
</ContentPage.Content>

И в моем коде позади у меня есть метод buttonSelectContact_Clicked:

private async void buttonSelectContact_Clicked(object sender, EventArgs e)
{
    Button btn = (Button)sender;
    btn.BackgroundColor = Color.FromHex("#22ac38");
    await Task.Delay(500);
    btn.BackgroundColor = Color.FromHex("#0080ff");
}

Он работает, но не так гладко, как я хочу. Можете ли вы предложить мне добавить анимацию вместо этого BackgroundColor?

Ответ 1

Согласно https://forums.xamarin.com/discussion/58818/how-to-animate-change-of-background-color, ваш код будет выглядеть так

XAML

<StackLayout Spacing="20" Padding="15">
    <Grid>
        <Button
        x:Name="buttonSelectContactSecond"
        Text="CLICK" BackgroundColor="#22ac38" />
        <Button
            x:Name="buttonSelectContact"
            Clicked="buttonSelectContact_Clicked"
            Text="CLICK" BackgroundColor="#0080ff" />
    </Grid>
</StackLayout>

В коде

private async void buttonSelectContact_Clicked(object sender, EventArgs e) 
{
    StartAnimation();
}

private async void StartAnimation()
{
    await Task.Delay(200);
    await buttonSelectContact.FadeTo(0, 250);
    await Task.Delay(200);
    await buttonSelectContact.FadeTo(1, 250);
}

Ответ 2

Я думаю, лучший выбор - создать функцию повторного использования. Вы можете использовать ресурсы анимации из .

Например, вы можете создать метод расширения, который предоставляет вам удобный и простой способ повторного использования в любом месте, например, FadeTo или TranslateTo...

Это довольно легко, за исключением логики, которая плавно меняет цвет в этом конкретном случае. Вот код:

public static class AnimationExtensions
{
    public static Task<bool> ChangeBackgroundColorTo(this Button self, Color newColor, uint length = 250, Easing easing = null)
    {
        Task<bool> ret = new Task<bool>(() => false);

        if (!self.AnimationIsRunning(nameof(ChangeBackgroundColorTo)))
        {
            Color fromColor = self.BackgroundColor;

            try
            {
                Func<double, Color> transform = (t) =>
                  Color.FromRgba(fromColor.R + t * (newColor.R - fromColor.R),
                                 fromColor.G + t * (newColor.G - fromColor.G),
                                 fromColor.B + t * (newColor.B - fromColor.B),
                                 fromColor.A + t * (newColor.A - fromColor.A));

                ret = TransmuteColorAnimation(self, nameof(ChangeBackgroundColorTo), transform, length, easing);
            }
            catch (Exception ex)
            {
                // to supress animation overlapping errors 
                self.BackgroundColor = fromColor;
            }
        }

        return ret;
    }

    private static Task<bool> TransmuteColorAnimation(Button button, string name, Func<double, Color> transform, uint length, Easing easing)
    {
        easing = easing ?? Easing.Linear;
        var taskCompletionSource = new TaskCompletionSource<bool>();

        button.Animate(name, transform, (color) => { button.BackgroundColor = color; }, 16, length, easing, (v, c) => taskCompletionSource.SetResult(c));
        return taskCompletionSource.Task;
    }
}

Импортируя этот класс (using namespace ссылку на using namespace), вы сможете использовать его, как показано ниже.

Объявление кнопки XAML:

<Button Text="Nice button ;)"
        BackgroundColor="Gray"
        x:Name="btnTeste"
        Clicked="btnTest_Click"/>

Обработка щелчка по коду:

private async void btnTest_Click(object sender, EventArgs args)
{
    #region You will not need this block, it is just to choose a random color for change to
    var colors = new[] { Color.Red, Color.Pink, Color.Silver, Color.Yellow, Color.Black, Color.Green };
    var rnd = new Random();

    var actualColor = btnTeste.BackgroundColor;
    var randomColor = colors.Where(c => c != actualColor).ToArray()[rnd.Next(0, colors.Length - 2)];
    #endregion

    // Here is the effective use of the smooth background color change animation
    await btnTeste.ChangeBackgroundColorTo(randomColor, 150, Easing.CubicOut);
    await btnTeste.ChangeBackgroundColorTo(actualColor, 100, Easing.SinOut);
}

РЕДАКТИРОВАТЬ:

Вот результат (gif показывает клики и двойные щелчки, поэтому вы можете видеть много сглаженных изменений):

effect example

Ответ 3

Вы также можете создать универсальный настраиваемый элемент управления с анимацией:

В этом примере я использовал Image control, но вы можете использовать Button, Label и т.д. :)

public class NativeImage : Image
{
    public static readonly BindableProperty CommandProperty =
        BindableProperty.Create(
            nameof(Command),
            typeof(ICommand),
            typeof(NativeImage),
            default(ICommand));

    public ICommand Command
    {
        get => (ICommand) GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }

    public static readonly BindableProperty CommandParameterProperty =
        BindableProperty.Create(
            nameof(Command),
            typeof(object),
            typeof(NativeImage));

    public object CommandParameter
    {
        get => GetValue(CommandParameterProperty);
        set => SetValue(CommandParameterProperty, value);
    }

    private ICommand TransitionCommand
    {
        get
        {
            return new Command(async () =>
            {
                AnchorX = 0.48;
                AnchorY = 0.48;
                await this.ScaleTo(0.8, 50, Easing.Linear);
                await Task.Delay(100);
                await this.ScaleTo(1, 50, Easing.Linear);
                Command?.Execute(CommandParameter);
            });
        }
    }

    public NativeImage()
    {
        Initialize();
    }

    public void Initialize()
    {
        GestureRecognizers.Add(new TapGestureRecognizer
        {
            Command = TransitionCommand
        });
    }
}