I want to implement a dialpad on my form.
Now, in my XAML I am testing a button:
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>
And in my code behind I have a method 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");
}
It works, but not quietly as smooth as I want.
Can you suggest me to add animation instead of this BackgroundColor?
According to https://forums.xamarin.com/discussion/58818/how-to-animate-change-of-background-color
your code would look like this
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>
In code behind
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);
}
I guess the better choice is to create a reusable feature to do that. You may take advantage of the animation resources from xamarin.forms.
For example, you can create an extension method that provides you a nice and easy way to reuse anywhere, like FadeTo
or TranslateTo
...
It is pretty easy, except for the logic that smoothly changes the color in this particular case. Here is the code:
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;
}
}
Importing this class (by the using namespace
reference) you will be able to use it like bellow.
Declaring an XAML
button:
<Button Text="Nice button ;)"
BackgroundColor="Gray"
x:Name="btnTeste"
Clicked="btnTest_Click"/>
Handling click in the code-behind:
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);
}
EDIT:
Here is the result (the gif shows clicks and double-clicks, so you can see a lot of smoothy changes):
You can also create the universal custom control with animation:
In this sample, I used Image control, but you can use Button, Label, etc. :)
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
});
}
}