Fehlende Inhaltsbildschirme in einer mobilen Anwendung anhand des Xamarin-Beispiels

Wie alles begann

Sehr oft müssen wir uns bei der Arbeit mit Unternehmensanwendungen mit solchen Bildschirmen befassen, wenn keine Daten vom Backend empfangen werden, wenn lediglich eine Liste auf dem Bildschirm angezeigt werden muss.





Dies ist normalerweise darauf zurückzuführen, dass entweder der Designer überhaupt nicht anwesend ist und alle Warframes in Form von Skizzen des Kunden vorliegen, oder dass es einen Designer gibt. Dieses Element wird jedoch für den Kunden als nicht so wichtig angesehen und einfach ignoriert.





, , . , .





-, . -, UI- - .





public class EmptyStateViewModel : ViewModel
{
    public EmptyStateViewModel(string image, string title, string description)
    {
        Image = image;
        Title = title;
        Description = description;
    }

    public string Image { get; }

    public string Title { get; }

    public string Description { get; }
}
      
      



( xaml Xamarin Forms) Bindings , , mvvm- .





?

- , - . - EmptyStateView, Retry, . EmptyStateViewModel, , .





public class ErrorStateViewModel : EmptyStateViewModel
{
    public ErrorStateViewModel(string image, string title, string description, string actionTitle, Command actionCommand)
        : base(image, title, description)
    {
        ActionTitle = actionTitle;
        ActionCommand = actionCommand;
    }

    public string ActionTitle { get; }

    public Command ActionCommand { get; }
}
      
      



?

- . . , -. None, null.





public static class OverlayFactory
{
    public static T None<T>()
        where T : EmptyStateViewModel
    {
        return null;
    }

    public static EmptyStateViewModel CreateCustom(string image, string title, string description)
    {
        return new EmptyStateViewModel(image, title, description);
    }

    public static ErrorStateViewModel CreateCustom(string image, string title, string description, string actionTitle, Command actionCommand)
    {
        return new ErrorStateViewModel(image, title, description, actionTitle, actionCommand);
    }
}
      
      



- -, , ,





public class SomeViewModel : BaseViewModel
{
    private IItemsLoadingService _itemsLoadingService;
    
    public SomeViewModel(IItemsLoadingService itemsLoadingService)
    {
        _itemsLoadingService = itemsLoadingService;
    }

    public ObservableCollection<ItemViewModel> Items { get; } = new ObservableCollection<ItemViewModel>();

    public EmptyStateViewModel EmptyState { get; protected set; }

    public ErrorStateViewModel ErrorState { get; protected set; }

    public override async Task InitializeAsync()
    {
        await base.InitializeAsync();

        await LoadItemsAsync();
    }

    private async Task LoadItemsAsync()
    {
        try
        {
            var result = await _itemsLoadingService.GetItemsAsync();
            var items = result.ToList();

            ErrorState = OverlayFactory.None<ErrorStateViewModel>();

            if (items.Count == 0)
            {
                EmptyState = OverlayFactory.CreateCustom("img_empty_state", "Title", "Description");
            }
            else
            {
                EmptyState = OverlayFactory.None<ErrorStateViewModel>();
                // Add items to list
            }
        }
        catch
        {
            ErrorState = OverlayFactory.CreateCustom("img_error_state", "Title", "Description", "Retry", new Command(() => LoadItemsAsync));
        }
    }
}
      
      



Binding EmptyState/ErrorState , mvvm-, , EmptyStateViewModel/ErrorStateViewModel null, . SetViewModel.





, View ViewModel View ViewState . ViewModel null - ViewState Gone, - Visible:





public void SetViewModel(EmptyStateViewModel viewModel)
{
    ViewModel = viewModel;

    View.Visibility = viewModel != null ? ViewStates.Visible : ViewStates.Gone;
}
      
      



iOS - constraints , - . enum, Android.





public void SetViewModel(EmptyStateViewModel viewModel)
{
    ViewModel = viewModel;

    View.SetVisibility(viewModel != null ? ViewStates.Visible : ViewStates.Gone);
}
      
      



extension





public static void SetVisibility(this UIView view, ViewVisibility visibility)
{
    var constraints = GetViewConstraints(view) ?? new NSLayoutConstraint[] {};

    if (visibility == ViewVisibility.Gone)
    {
        SaveViewConstraints(view, constraints);
        NSLayoutConstraint.DeactivateConstraints(constraints);
        view.Hidden = true;
        return;
    }
  
    if (visibility == ViewVisibility.Visible)
    {
        SaveViewConstraints(view, null);
        NSLayoutConstraint.ActivateConstraints(constraints);
        view.Hidden = false;
        return;
    }
}
      
      



Hier speichern wir beim Festlegen von ViewVisibility.Gone die Einschränkungen unserer Ansicht vorab und deaktivieren sie. Wenn die Sichtbarkeit aktiviert ist, nehmen wir im Gegenteil die zuvor gespeicherten Einschränkungen heraus, setzen die Speicherung zurück und aktivieren sie dann.





private static NSLayoutConstraint[] GetViewConstraints(UIView view)
{
    return view.GetAssociatedObject<NSMutableArray<NSLayoutConstraint>>(Key)?.ToArray() ??
           view.Superview?.Constraints
               .Where(constraint => (constraint.FirstItem?.Equals(view) == true) || constraint.SecondItem.Equals(view))
               .ToArray();
}

private static void SaveViewConstraints(UIView view, NSLayoutConstraint[] constraints)
{
    NSMutableArray<NSLayoutConstraint> viewConstraints = null;

    if (constraints.Length > 0)
    {
        viewConstraints = new NSMutableArray<NSLayoutConstraint>();
        viewConstraints.AddObjects(constraints);
    }

    view.SetAssociatedObject(Key, viewConstraints, AssociationPolicy.RetainNonAtomic);
}
      
      



Mit der ersten Methode können Sie zuvor gespeicherte Einschränkungen abrufen oder, falls nicht, die aktuellen abrufen. Wenn keine übergeordnete Ansicht vorhanden ist, wird null zurückgegeben.





Bei der zweiten Methode werden die aktuellen Einschränkungen gespeichert, damit sie später wiederhergestellt werden können.





So stellte sich heraus, dass angenehmere Bildschirme mit fehlenden Daten oder Fehlerstatusbildschirme erstellt wurden.





PS - der erste Artikel über Habré, daher nicht streng beurteilen. Aber du musst irgendwo anfangen.








All Articles