Xamarin.Forms: how to display a Lottie animation at least one time when IsBusy is true during data loading?

后端 未结 2 1508
广开言路
广开言路 2021-01-28 07:03

On my Xamarin.Forms project, I would like to display a Lottie animation during API calls or during the loading of a we

相关标签:
2条回答
  • 2021-01-28 07:29

    I've found another approach that works, even if this solution is a bit heavy and can be improved.

    Firstly, as recommended there, I've created 2 Triggers:

    public class PlayLottieAnimationTriggerAction : TriggerAction<AnimationView>
    {
        protected override void Invoke(AnimationView sender)
        {
            Debug.WriteLine($"PlayLottieAnimationTriggerAction()");
            sender.PlayAnimation();
        }
    }
    
    public class StopLottieAnimationTriggerAction : TriggerAction<AnimationView>
    {
        protected override void Invoke(AnimationView sender)
        {
            Debug.WriteLine($"StopLottieAnimationTriggerAction()");
            sender.StopAnimation();
        }
    }
    

    I also used EventToCommandBehaviors, like described there.

    After this I can use the Lottie animation like this:

    <forms:AnimationView 
        x:Name="animationView" 
        BackgroundColor="Transparent"
        AutoPlay="True"
        IsVisible="{Binding ShowAnimation}"
        Animation="resource://lottie_4squares_apricot_blond.json?assembly=Example.Forms"
        AnimationSource="EmbeddedResource"
        VerticalOptions="FillAndExpand" 
        HorizontalOptions="FillAndExpand">
        <forms:AnimationView.Triggers>
            <MultiTrigger TargetType="forms:AnimationView">
                <MultiTrigger.Conditions>
                    <BindingCondition Binding="{Binding ShowAnimation}" Value="True" />
                </MultiTrigger.Conditions>
                <MultiTrigger.EnterActions>
                    <triggers:LottieTriggerAction />
                </MultiTrigger.EnterActions>
                <MultiTrigger.ExitActions>
                    <actions:StopLottieAnimationTriggerAction />
                </MultiTrigger.ExitActions>
            </MultiTrigger>
        </forms:AnimationView.Triggers>
        <forms:AnimationView.Behaviors>
            <behaviors:EventToCommandBehavior
                EventName="OnFinishedAnimation"
                Command="{Binding OnFinishedAnimationCommand}"
                CommandParameter="{x:Reference animationView}"/>
        </forms:AnimationView.Behaviors>
    </forms:AnimationView>
    

    And in my ViewModel, I've declared a property ShowAnimation that is related to IsBusy and the Command OnFinishedAnimationCommand like this:

    private bool _showAnimation;
    public bool ShowAnimation
    {
        get => _showAnimation;
        set => Set(ref _showAnimation, value);
    }
    
    public ICommand OnFinishedAnimationCommand
    {
        get
        {
            return new Xamarin.Forms.Command<object>(async (object sender) =>
            {
                if (sender != null)
                {
                    await OnFinishedAnimation(sender);
                }
            });
        }
    }
    
    private Task OnFinishedAnimation(object sender)
    {
        var view = sender as AnimationView;
        if (IsBusy)
        {
            view.PlayAnimation();
        }
        else
        {
            ShowAnimation = false;
        }
        return Task.CompletedTask;
    }
    

    In case of the Loader is related to a WebView, the ShowLoadingView property is set like this:

    private Task WebViewNavigatingAsync(WebNavigatingEventArgs eventArgs)
    {
        IsBusy = true;
        ShowLoadingView = true;
        return Task.CompletedTask;
    }
    
    private async Task WebViewNavigatedAsync(WebNavigatedEventArgs eventArgs)
    {
        IsBusy = false;
    }
    

    But, as I also display an ErrorView in case of issues (timeout, unreachable server, ...) and a Reload/Retry button, I had to add some code:

    private async Task WebViewNavigatedAsync(WebNavigatedEventArgs eventArgs)
    {
        IsBusy = false;
        // for display loading animation on Refresh
        while (ShowLoadingView)
            await Task.Delay(50);
        SetServiceError();
    }
    

    In case of the Loader is related to Data loading, the ShowLoadingView property is set like this:

    private async Task GetNewsAsync(bool forceRefresh = false)
    {
        try
        {
            ShowErrorView = false;
            ErrorKind = ServiceErrorKind.None;
            IsBusy = true;
            ShowLoadingView = true;
            var _news = await _dataService.GetNews(forceRefresh);
            News = new ObservableCollection<News>(_news);
        }
        catch (Exception ex)
        {
            ErrorKind = ServiceErrorKind.ServiceIssue;
        }
        finally
        {
            IsBusy = false;
            await SetServiceError();
        }
    

    }

    However, I noticed that in some cases the SetServiceError() was not fired, as OnFinishedAnimation() was called in the same time. I haven't yet investigated, but I've fixed this by adding the call to SetServiceError() in in OnFinishedAnimation():

    private async Task OnFinishedAnimation(object sender)
    {
        var view = sender as AnimationView;
        if (IsBusy)
        {
            view.PlayAnimation();
        }
        else
        {
            ShowLoadingView = false;
            // fix SetServiceError() call issue
            await SetServiceError();
        }
    }
    

    Don't hesitate to tell what could be done to optimize this.

    0 讨论(0)
  • 2021-01-28 07:43

    I would like to display a Lottie animation during API calls

    public async void loadData()
    {
        //Data load started
        viewModel.IsBusy = true;
    
        await methodOfLoadingData...;
    
        //Data load finished
         viewModel.IsBusy = false;
    }
    

    during the loading of a website in a WebView:

    private void MyWebView_Navigating(object sender, WebNavigatingEventArgs e)
    {
        viewModel.IsBusy = true;
    }
    
    private void MyWebView_Navigated(object sender, WebNavigatedEventArgs e)
    {
        viewModel.IsBusy = false;
    }
    

    But the loading duration is sometimes very short

    The loading duration is depending on the time you completely loading the data/webview. If you load the data/webview very fast, the loading duration should be short.

    0 讨论(0)
提交回复
热议问题