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

后端 未结 2 1558
广开言路
广开言路 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
    {
        protected override void Invoke(AnimationView sender)
        {
            Debug.WriteLine($"PlayLottieAnimationTriggerAction()");
            sender.PlayAnimation();
        }
    }
    
    public class StopLottieAnimationTriggerAction : TriggerAction
    {
        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:

    
        
            
                
                    
                
                
                    
                
                
                    
                
            
        
        
            
        
    
    

    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(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);
        }
        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.

    提交回复
    热议问题