If you are already using Xamarin.Forms in real projects, then you are probably faced with the built-in animation mechanism. If not, then we recommend starting the acquaintance with the articles “Creating Animations with Xamarin.Forms” and “Compound Animations” .
Most often you want to animate the following properties:
In Xamarin.Forms, low-level OS mechanisms are used to set the indicated properties, which has a great effect on performance - there is no problem to animate a whole bunch of objects at once. In our example, we will focus on these properties, but if you wish, you can independently expand the mechanisms described below.
If we describe a finite automaton in human language, then this is an object that may be in different stable states (for example, “loading” or “error”). Their state machine changes under the influence of external events. The number of states of course. Examples of such machines are elevators and traffic lights.
If for some reason you did not study at the institute on a technical specialty or did not study theory, then we recommend starting your acquaintance with this article .
What does all this have to do with animations and especially with Xamarin.Forms? Let's get a look.
In the article “Working with screen states in Xamarin.Forms”, we have already described the StateContainer component, which simplifies the development of complex interfaces and is suitable for most screens in business applications. This component works well when we have all the states independently of each other and a rather simple transition “one disappeared - the second appeared” between them.
But what if you need to implement a complex and animated transition from one state to another? To go, rotate and jump.
As an example, let's look at the screen for entering an address and working with a map, as it is implemented in most navigators.
Imagine that we have animated transitions between the following states of a SINGLE screen:
As you can see, we have such a state machine:
You need to implement the following animations when transitioning from state to state:
We will take the simplest implementation:
We will not keep any history of transitions, neither does it matter for which user event the machine has moved from one state to another. There is only a transition to a new state, which is accompanied by animations.
So, the simplest machine gun, which we call the Storyboard, will look like this:
public enum AnimationType { Scale, Opacity, TranslationX, TranslationY, Rotation } public class Storyboard { readonly Dictionary<string, ViewTransition[]> _stateTransitions = new Dictionary<string, ViewTransition[]>(); public void Add(object state, ViewTransition[] viewTransitions) { var stateStr = state?.ToString().ToUpperInvariant(); _stateTransitions.Add(stateStr, viewTransitions); } public void Go(object newState, bool withAnimation = true) { var newStateStr = newState?.ToString().ToUpperInvariant(); // Get all ViewTransitions var viewTransitions = _stateTransitions[newStateStr]; // Get transition tasks var tasks = viewTransitions.Select(viewTransition => viewTransition.GetTransition(withAnimation)); // Run all transition tasks Task.WhenAll(tasks); } } public class ViewTransition { // Skipped. See complete sample in repository below public async Task GetTransition(bool withAnimation) { VisualElement targetElement; if( !_targetElementReference.TryGetTarget(out targetElement) ) throw new ObjectDisposedException("Target VisualElement was disposed"); if( _delay > 0 ) await Task.Delay(_delay); withAnimation &= _length > 0; switch ( _animationType ) { case AnimationType.Scale: if( withAnimation ) await targetElement.ScaleTo(_endValue, _length, _easing); else targetElement.Scale = _endValue; break; // See complete sample in repository below default: throw new ArgumentOutOfRangeException(); } } }
In the example above, input checks are omitted, the full version can be found in the repository (link at the end of the article).
As we see, in the transition to a new state, smooth changes in the required properties just occur in parallel. It is also possible to go to a new state without animation.
So, we have an automaton and we can connect it to set the necessary state of the elements. An example of adding a new state:
_storyboard.Add(States.Drive, new[] { new ViewTransition(ShowRouteView, AnimationType.TranslationY, 200), new ViewTransition(ShowRouteView, AnimationType.Opacity, 0, 0, delay: 250), new ViewTransition(DriveView, AnimationType.TranslationY, 0, 300, delay: 250), // Active and visible new ViewTransition(DriveView, AnimationType.Opacity, 1, 0) // Active and visible });
As you can see, for the Drive state, we set an array of individual animations. ShowRouteView and DriveView are the normal views defined in XAML, as shown below.
But to switch to a new state, simply call the Go () method:
_storyboard.Go(States.ShowRoute);
The code turns out to be relatively small and group animations are created in fact simply by a set of numbers. Our finite state machine can work not only with pages, but also with individual View, which expands its application options. It is better to use Storyboard inside the page code (Page), without mixing it with business logic.
We also give an example of XAML, which describes all the elements of the user interface.
If you decide to add the ability to change the color of elements using animations, we recommend that you familiarize yourself with the implementation described in the article “Building Custom Animations in Xamarin.Forms” .
The full project code from the article can be found in our repository:
https://bitbucket.org/binwell/statemachine .
And as always, ask your questions in the comments. Before communication!
Source: https://habr.com/ru/post/335418/
All Articles