Como tudo começou
Muitas vezes, ao trabalhar com aplicativos Enterprise, temos que lidar com essas telas na ausência de dados recebidos do back-end, quando simplesmente precisamos exibir uma lista na tela.
, , , , , .
, , . , .
-, . -, 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;
}
}
Aqui, no caso de definir ViewVisibility.Gone, nós pré-salvamos as restrições de nossa visualização e as desativamos, e quando a visibilidade é ativada, ao contrário, obtemos as restrições salvas anteriormente, redefinimos a preservação e, em seguida, ativamos.
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);
}
O primeiro método permite obter restrições salvas anteriormente, se houver, ou, se não, obter as atuais. Se não houver visualização pai, será retornado nulo.
O segundo método salvará as restrições atuais para que possam ser restauradas posteriormente.
Assim, acabou tornando mais agradáveis as telas com dados faltantes, ou telas de status de erros.
PS - o primeiro artigo sobre Habré, portanto, não julgue estritamente. Mas você precisa começar de algum lugar.