Skip to content

Repository files navigation

Sharpnado.CollectionView(.Maui)

Formerly named HorizontalListView

Get it from NuGet:

MAUI Supported platformsXF Supported platforms
NugetNuget
✔️ Android✔️ Android
✔️ iOS✔️ iOS

Presentation

Initialization

MAUI

  • In MauiProgram.cs:
publicstaticMauiAppCreateMauiApp(){varbuilder=MauiApp.CreateBuilder();builder.UseMauiApp().UseSharpnadoCollectionView(loggerEnable:false);}

Xamarin.Forms

  • On Core project in App.xaml.cs:

For the namespace schema to work, you need to call initializer from App.xaml.cs like this:

publicApp(){InitializeComponent();Sharpnado.CollectionView.Initializer.Initialize(true,false);
...}
  • On iOS add this line before Xamarin.Forms.Forms.Init() and LoadApplication(new App()).
publicoverrideboolFinishedLaunching(UIApplicationapp,NSDictionaryoptions){Initializer.Initialize();global::Xamarin.Forms.Forms.Init();LoadApplication(newApp());}
  • On Android add this line before Xamarin.Forms.Forms.Init() and LoadApplication(new App()).
publicoverrideOnCreate(Bundle savedInstanceState){Initializer.Initialize();global::Xamarin.Forms.Forms.Init();LoadApplication(newApp());}

Version 2.0 breaking changes: CollectionView

HorizontalListView has finally been renamed CollectionView \o/.

All references to HorizontalList has been renamed to Collection, including:

  • namespaces
  • filename
  • class names
  • HorizontalListViewLayout => CollectionViewLayout
  • ListLayout => CollectionLayout

Presentation

  • Horizontal, Grid, Carousel or Vertical layout
  • Drag and Drop feature
  • Grouping with headers and footers
  • Reveal custom animations
  • Column count
  • Infinite loading with Paginator component
  • Snapping on first or middle element
  • Padding and item spacing
  • Handles NotifyCollectionChangedAction Add, Remove and Reset actions
  • View recycling
  • RecyclerView on Android
  • UICollectionView on iOS
demo.mp4

Horizontal list layout

publicCollectionViewLayoutCollectionLayout{get;set;}=CollectionViewLayout.Horizontal;

By default the layout is in Linear mode, which means you will have only one row. You can specify the ItemWidth and ItemHeight. You can also specify ItemSpacing and CollectionPadding.

<DataTemplatex:Key="HorizontalDudeTemplate">
<sho:DraggableViewCellx:Name="DraggableViewCell">
<ContentView
xamEffects:Commands.Tap="{Binding TapCommand}"
xamEffects:Commands.TapParameter="{Binding .}"
xamEffects:TouchEffect.Color="{StaticResource Accent}">
<sho:Shadowsx:Name="Shadow"CornerRadius="10"Shades="{StaticResource DarkerNeumorphism}">
<views:SillyHorizontalCellMargin="16,13,16,13"BackgroundColor="{StaticResource DarkerSurface}"CornerRadius="10">
<views:SillyHorizontalCell.Triggers>
<DataTriggerBinding="{Binding Source={x:Reference DraggableViewCell}, Path=IsDragAndDropping}"TargetType="views:SillyHorizontalCell"Value="True">
<SetterProperty="BackgroundColor"Value="{StaticResource DarkSurface}" />
</DataTrigger>
</views:SillyHorizontalCell.Triggers>
</views:SillyHorizontalCell>
</sho:Shadows>
</ContentView>
</sho:DraggableViewCell>
</DataTemplate>
...
<sho:CollectionViewx:Name="CollectionView"CollectionPadding="10,30,10,75"CurrentIndex="{Binding CurrentIndex}"InfiniteListLoader="{Binding SillyPeoplePaginator}"ItemHeight="260"ItemWidth="260"ItemTemplate="{StaticResource HorizontalDudeTemplate}"ItemsSource="{Binding SillyPeople}"CollectionLayout="Horizontal"ScrollBeganCommand="{Binding OnScrollBeginCommand}"ScrollEndedCommand="{Binding OnScrollEndCommand}"SnapStyle="Center"TapCommand="{Binding TapCommand}" />

You can also simply use the the HorizontalListView class which is a shorthand to set the layout to Horizontal.

<sho:HorizontalListViewCollectionPadding="10,30,10,75"CurrentIndex="{Binding CurrentIndex}"InfiniteListLoader="{Binding SillyPeoplePaginator}"ItemHeight="260"ItemWidth="260"ItemTemplate="{StaticResource HorizontalDudeTemplate}"ItemsSource="{Binding SillyPeople}"ScrollBeganCommand="{Binding OnScrollBeginCommand}"ScrollEndedCommand="{Binding OnScrollEndCommand}"SnapStyle="Center"TapCommand="{Binding TapCommand}" />

As you can see TapCommand and TouchFeedbackColor (aka Ripple) are brought to you by the awesome effects created by mrxten (https://github.com/mrxten/XamEffects). It's the best ripple effect plugin so far on Xamarin.Forms since it always worked for me. With other maybe more known plugins, I had some issues on iOS.

A CollectionView with SnapStyle=Center and ItemWidth/ItemHeight set.

ColumnCount property

You can also decide to just specify the number of column you want, the ColumnCount property, and the ItemWidth will be computed for you.

<sho:CollectionViewx:Name="CollectionView"CollectionPadding="10,30,10,75"ColumnCount="2"CurrentIndex="{Binding CurrentIndex}"InfiniteListLoader="{Binding SillyPeoplePaginator}"ItemHeight="260"ItemTemplate="{StaticResource DudeTemplateSelector}"ItemsSource="{Binding SillyPeople}"ListLayout="Linear"TapCommand="{Binding TapCommand}" />

A CollectionView with ColumnCount=2.

Carousel Layout

You can set ListLayout to Carousel. In this mode you can't specify ItemWidth (obviously). If you don't specify the ItemHeight, it will be automatically computed for you.

<renderedViews:CollectionView Grid.Row="3"Margin="-16,8"CollectionPadding="8,8"ItemSpacing="8"ListLayout="Carousel"ItemsSource="{Binding SillyPeopleLoader.Result}"SnapStyle="Center">
...
</renderedViews:CollectionView>

A CollectionView with ListLayout=Carousel.

Grid Layout

If you set the ListLayout property to Grid, you will have access to the same properties.

<DataTemplatex:Key="GridDudeTemplate">
<sho:DraggableViewCellx:Name="DraggableViewCell">
<ContentView>
<sho:Shadowsx:Name="Shadow"CornerRadius="10"Shades="{StaticResource ThinDarkerNeumorphism}">
<views:SillyGridCellMargin="16,13,16,13"BackgroundColor="{StaticResource DarkerSurface}"CornerRadius="10">
<views:SillyGridCell.Triggers>
<DataTriggerBinding="{Binding Source={x:Reference DraggableViewCell}, Path=IsDragAndDropping}"TargetType="views:SillyGridCell"Value="True">
<SetterProperty="BackgroundColor"Value="{StaticResource DarkSurface}" />
</DataTrigger>
</views:SillyGridCell.Triggers>
</views:SillyGridCell>
</sho:Shadows>
</ContentView>
</sho:DraggableViewCell>
</DataTemplate>
<sho:CollectionViewx:Name="CollectionView"CollectionPadding="10,30,10,75"CurrentIndex="{Binding CurrentIndex}"EnableDragAndDrop="True"InfiniteListLoader="{Binding SillyPeoplePaginator}"ItemHeight="120"ItemWidth="120"ItemTemplate="{StaticResource GridDudeTemplate}"ItemsSource="{Binding SillyPeople}"ListLayout="Grid"TapCommand="{Binding TapCommand}" />

You can use the IsDragAndDropping property of the DraggableViewCell to change the background color with a simple DataTrigger.

A GridListLayout with drag and drop enabled.

The ColumnCount property works also with the grid layout.

Vertical Layout

You can also use Sharpnado's CollectionView like a regular list view.

<DataTemplatex:Key="VerticalDudeTemplate">
<sho:DraggableViewCellx:Name="DraggableViewCell">
<ContentView>
<sho:Shadowsx:Name="Shadow"CornerRadius="10"Shades="{StaticResource ThinDarkerNeumorphism}">
<views:SillyListCellMargin="16,13,16,13"BackgroundColor="{StaticResource DarkerSurface}"CornerRadius="10">
<views:SillyListCell.Triggers>
<DataTriggerBinding="{Binding Source={x:Reference DraggableViewCell}, Path=IsDragAndDropping}"TargetType="views:SillyListCell"Value="True">
<SetterProperty="BackgroundColor"Value="{StaticResource DarkSurface}" />
</DataTrigger>
</views:SillyListCell.Triggers>
</views:SillyListCell>
</sho:Shadows>
</ContentView>
</sho:DraggableViewCell>
</DataTemplate>
<sho:CollectionViewx:Name="CollectionView"CollectionPadding="10,30,10,75"CurrentIndex="{Binding CurrentIndex}"EnableDragAndDrop="True"InfiniteListLoader="{Binding SillyPeoplePaginator}"ItemHeight="120"ItemTemplate="{StaticResource VerticalDudeTemplate}"ItemsSource="{Binding SillyPeople}"ListLayout="Vertical"TapCommand="{Binding TapCommand}" />

A CollectionView with ListLayout=Vertical.

Of course drag and drop is also available with this layout.

Infinite Loading

You can achieve infinite loading really easily by using the Paginator component, and bind it to the InfiniteListLoader property. All is explained here:

https://www.sharpnado.com/paginator-platform-independent/

Drag and drop

If you want to have both drag and drop enabled and still be able to tap the item, you need to use the TapCommand on the CollectionView instead of the xamEffects:Commands.Tap on the DataTemplate content. It's less nice since you won't have the nice color ripple, but it will work :)

The only thing you have to do to enable drag and drop is set EnableDragAndDrop to true.

The DragAndDropStartCommand and DragAndDropEndedCommand commands will pass as argument a DragAndDropInfo object:

publicclassDragAndDropInfo{publicintTo{get;}publicintFrom{get;}publicobjectContent{get;}}

Contributor: Implemented by @jmmortega.

DragAndDropTrigger and DragAndDropDirection

Since 1.8.2, you can now choose if you want to begin the drag and drop with a Pan gesture or a LongPress.

  • DragAndDropTrigger="Pan"
  • DragAndDropTrigger="LongTap"

You can also restrict the drag movement to a given direction:

  • For the horizontal layout: DragAndDropDirection = HorizontalOnly
  • For the vertical layout: DragAndDropDirection = VerticalOnly

It will give a better more precise drag experience, more precise.

Since 1.8.1

EnableDragAndDrop is now a bindable property, so you can enable it at runtime.

You can now also specify a custom animation when the EnableDragAndDrop is set to ture:

CollectionView.DragAndDropEnabledAnimationAsync=async(viewCell,token)=>{while(!token.IsCancellationRequested){awaitviewCell.View.RotateTo(8);awaitviewCell.View.RotateTo(-8);}awaitviewCell.View.RotateTo(0);};

will result in:

You can decide to start the drag without long press on iOS thanks to the iOS specific property iOSDragAndDropOnPanGesture:

<sho:CollectionView
...
iOSDragAndDropOnPanGesture="True" />

Remark: You don't have to inherit from DraggableViewCell, any ViewCell can be dragged.

DraggableViewCell

The DraggableViewCell is useful for using triggers on the IsDragAndDropping property (changing its background color or elevation during drag and drop for example).

You can also disable the drag and drop for certain cells thanks to the IsDraggable property.

Headers, groups and footers (only for linear layouts)

Since 2.0, you can assign a size to a DataTemplate using the SizedDataTemplate markup extension. This opens the door to the implementation of header/footer/group headers.

All you have to do is to use a DataTemplateSelector with SizedDataTemplate and set the size of the given DataTemplate.

Let's consider the following screen:

In our example, we want, a header, a footer, but also a group header (items are grouped by silliness degree, their "star" rating). So we will be using inheritance on the view model side to achieve that:

namespaceDragAndDropSample.ViewModels{publicinterfaceIDudeItem{}publicclassDudeHeader:IDudeItem{}publicclassDudeFooter:IDudeItem{}publicclassDudeGroupHeader:IDudeItem{publicintStarCount{get;set;}publicstringText=>$"{StarCount} Stars";}publicclassSillyDudeVmo:IDudeItem{publicSillyDudeVmo(SillyDudedude,ICommandtapCommand){if(dude!=null){Id=dude.Id;Name=dude.Name;FullName=dude.FullName;Role=dude.Role;Description=dude.Description;ImageUrl=dude.ImageUrl;SillinessDegree=dude.SillinessDegree;SourceUrl=dude.SourceUrl;}TapCommand=tapCommand;}publicboolIsMovable{get;protectedset;}=true;publicICommandTapCommand{get;set;}publicintId{get;}publicstringName{get;}publicstringFullName{get;}publicstringRole{get;}publicstringDescription{get;}publicstringImageUrl{get;}publicdoubleSillinessDegree{get;}publicstringSourceUrl{get;}publicoverridestringToString(){return$"{FullName} silly degree: {SillinessDegree}";}}}

Then after sorting our collection by rating, we will bind our CollectionView to the SillyPeople list.

publicclassHeaderFooterGroupingPageViewModel:ANavigableViewModel{publicList<IDudeItem>SillyPeople{get=>_sillyPeople;set=>SetAndRaise(ref_sillyPeople,value);}privateasyncTask<PageResult<SillyDude>>LoadSillyPeoplePageAsync(intpageNumber,intpageSize,boolisRefresh){PageResult<SillyDude>resultPage=await_sillyDudeService.GetSillyPeoplePage(pageNumber,pageSize);vardudes=resultPage.Items;if(isRefresh){SillyPeople=newList<IDudeItem>();_listSource=newList<SillyDude>();}varresult=newList<IDudeItem>{newDudeHeader()};_listSource.AddRange(dudes);foreach(vargroupin_listSource.OrderByDescending(d =>d.SillinessDegree).GroupBy((dude)=>dude.SillinessDegree)){result.Add(newDudeGroupHeader{StarCount=group.Key});result.AddRange(group.Select(dude =>newSillyDudeVmo(dude,TapCommand)));}result.Add(newDudeFooter());SillyPeople=result;}}

Thanks god for Linq!

You can see how easy it is to order and create our header view models.

Now let's switch to the XAML world!

We create a template for each of our header types:

 <ResourceDictionary>
<DataTemplatex:Key="HeaderTemplate">
<sho:DraggableViewCellx:Name="DraggableViewCell"IsDraggable="False">
<ContentViewMargin="0"BackgroundColor="{StaticResource DarkerSurface}">
<LabelStyle="{StaticResource TextSubhead}"HorizontalOptions="Center"Text="Look at my Nice Header!" />
</ContentView>
</sho:DraggableViewCell>
</DataTemplate>
<DataTemplatex:Key="FooterTemplate">
<sho:DraggableViewCellx:Name="DraggableViewCell"IsDraggable="False">
<StackLayoutPadding="30,0,15,0"Orientation="Horizontal"Spacing="15">
<ActivityIndicatorVerticalOptions="Center"IsRunning="True"Color="{StaticResource Accent}" />
<LabelStyle="{StaticResource TextSubhead}"VerticalOptions="Center"Text="Loading next dudes..." />
</StackLayout>
</sho:DraggableViewCell>
</DataTemplate>
<DataTemplatex:Key="GroupHeaderTemplate"x:DataType="viewModels:DudeGroupHeader">
<sho:DraggableViewCellx:Name="DraggableViewCell"IsDraggable="False">
<sho:Shadowsx:Name="Shadow"Shades="{StaticResource VerticalNeumorphism}">
<StackLayoutMargin="0,15,0,10"Padding="0"BackgroundColor="{StaticResource DarkerSurface}"Orientation="Horizontal"Spacing="0">
<FrameWidthRequest="30"HeightRequest="30"Margin="15,0,10,0"Padding="0"HorizontalOptions="End"VerticalOptions="Center"BackgroundColor="{StaticResource Accent}"CornerRadius="10"HasShadow="False">
<LabelStyle="{StaticResource TextTitle}"HorizontalOptions="Center"VerticalOptions="Center"Text="{Binding StarCount}" />
</Frame>
<LabelStyle="{StaticResource TextTitle}"VerticalOptions="Center"Text="Stars Dudes" />
</StackLayout>
</sho:Shadows>
</sho:DraggableViewCell>
</DataTemplate>
<DataTemplatex:Key="DudeTemplate">
<sho:DraggableViewCellx:Name="DraggableViewCell">
<sho:Shadowsx:Name="Shadow"CornerRadius="10"Shades="{StaticResource ThinDarkerNeumorphism}">
<views:SillyListCellMargin="16,13"BackgroundColor="{StaticResource DarkerSurface}"CornerRadius="10">
<views:SillyListCell.Triggers>
<DataTriggerBinding="{Binding Source={x:Reference DraggableViewCell}, Path=IsDragAndDropping}"TargetType="views:SillyListCell"Value="True">
<SetterProperty="BackgroundColor"Value="{StaticResource DarkSurface}" />
</DataTrigger>
</views:SillyListCell.Triggers>
</views:SillyListCell>
</sho:Shadows>
</sho:DraggableViewCell>
</DataTemplate>
</ResourceDictionary>

The last step is to make the correspondance between our header view models, and our headers data templates. For that, we declare our DataTemplateSelector:

publicclassHeaderFooterGroupingTemplateSelector:DataTemplateSelector{publicSizedDataTemplateHeaderTemplate{get;set;}publicSizedDataTemplateFooterTemplate{get;set;}publicSizedDataTemplateGroupHeaderTemplate{get;set;}publicDataTemplateDudeTemplate{get;set;}protectedoverrideDataTemplateOnSelectTemplate(objectitem,BindableObjectcontainer){switch(item){caseDudeHeaderheader:returnHeaderTemplate;caseDudeFooterfooter:returnFooterTemplate;caseDudeGroupHeadergroupHeader:returnGroupHeaderTemplate;default:returnDudeTemplate;}}}

You can see that all the headers (all the data template with an associated size in fact) need to be a SizedDataTemplate. Then we just assign a fixed size to each template when we declare our DataTemplateSelector :

<views:HeaderFooterGroupingTemplateSelectorx:Key="HeaderFooterGroupingTemplateSelector"DudeTemplate="{StaticResource DudeTemplate}"FooterTemplate="{sho:SizedDataTemplate Template={StaticResource FooterTemplate}, Size=60}"GroupHeaderTemplate="{sho:SizedDataTemplate Template={StaticResource GroupHeaderTemplate}, Size=75}"HeaderTemplate="{sho:SizedDataTemplate Template={StaticResource HeaderTemplate}, Size=40}" />

We don't have to assign a size to our item template (here the silly dude), it will pick the ItemWidth (for an horizontal layout) or ItemHeight (for a vertical one) size.

header_demo.mp4

You can find this example in the sample project (click on "Header and Grouping Example" button).

Reveal animations

Contributor: original idea from @jmmortega.

You can set custom animations on cells that will be triggered when a cell appears for the first time.

Properties for reveal animations

publicFunc<ViewCell,Task>PreRevealAnimationAsync{get;set;}publicFunc<ViewCell,Task>RevealAnimationAsync{get;set;}publicFunc<ViewCell,Task>PostRevealAnimationAsync{get;set;}

In the following example I flip the cell on the vertical axis and fade them for grid and linear layout. And flip the cell on the horizontal axis for vertical layout.

GridPage.xaml.cs

publicpartialclassGridPage:ContentPage{publicGridPage(){InitializeComponent();CollectionView.PreRevealAnimationAsync=async(viewCell)=>{viewCell.View.Opacity=0;if(CollectionView.CollectionLayout==CollectionViewLayout.Vertical){viewCell.View.RotationX=90;}else{viewCell.View.RotationY=-90;}};CollectionView.RevealAnimationAsync=async(viewCell)=>{awaitviewCell.View.FadeTo(1);if(CollectionView.CollectionLayout==CollectionViewLayout.Vertical){awaitviewCell.View.RotateXTo(0);}else{awaitviewCell.View.RotateYTo(0);}};}}

Others properties

Properties available with both layout mode

publicstaticreadonlyBindablePropertyListLayoutProperty=BindableProperty.Create(nameof(ListLayout),typeof(CollectionViewLayout),typeof(CollectionView),CollectionViewLayout.Linear,propertyChanged:OnListLayoutChanged,propertyChanging:OnListLayoutChanging);publicstaticreadonlyBindablePropertyItemsSourceProperty=BindableProperty.Create(nameof(ItemsSource),typeof(IEnumerable),typeof(CollectionView),default(IEnumerable<object>),BindingMode.TwoWay,propertyChanged:OnItemsSourceChanged);publicstaticreadonlyBindablePropertyInfiniteListLoaderProperty=BindableProperty.Create(nameof(InfiniteListLoader),typeof(IInfiniteListLoader),typeof(CollectionView));publicstaticreadonlyBindablePropertyItemTemplateProperty=BindableProperty.Create(nameof(ItemTemplate),typeof(DataTemplate),typeof(CollectionView),default(DataTemplate));publicstaticreadonlyBindablePropertyItemHeightProperty=BindableProperty.Create(nameof(ItemHeight),typeof(double),typeof(CollectionView),defaultValue:0D,defaultBindingMode:BindingMode.OneWayToSource);publicstaticreadonlyBindablePropertyItemWidthProperty=BindableProperty.Create(nameof(ItemWidth),typeof(double),typeof(CollectionView),defaultValue:0D,defaultBindingMode:BindingMode.OneWayToSource);publicstaticreadonlyBindablePropertyCollectionPaddingProperty=BindableProperty.Create(nameof(CollectionPadding),typeof(Thickness),typeof(CollectionView),defaultValue:newThickness(0,0),defaultBindingMode:BindingMode.OneWayToSource);publicstaticreadonlyBindablePropertyItemSpacingProperty=BindableProperty.Create(nameof(ItemSpacing),typeof(int),typeof(CollectionView),defaultValue:0,defaultBindingMode:BindingMode.OneWayToSource);publicstaticreadonlyBindablePropertyTapCommandProperty=BindableProperty.Create(nameof(TapCommand),typeof(ICommand),typeof(CollectionView));publicstaticreadonlyBindablePropertyScrollBeganCommandProperty=BindableProperty.Create(nameof(ScrollBeganCommand),typeof(ICommand),typeof(CollectionView));publicstaticreadonlyBindablePropertyScrollEndedCommandProperty=BindableProperty.Create(nameof(ScrollEndedCommand),typeof(ICommand),typeof(CollectionView));publicstaticreadonlyBindablePropertyCurrentIndexProperty=BindableProperty.Create(nameof(CurrentIndex),typeof(int),typeof(CollectionView),defaultValue:-1,defaultBindingMode:BindingMode.TwoWay,propertyChanged:OnCurrentIndexChanged);publicstaticreadonlyBindablePropertyVisibleCellCountProperty=BindableProperty.Create(nameof(VisibleCellCount),typeof(int),typeof(CollectionView),defaultValue:0,defaultBindingMode:BindingMode.TwoWay,propertyChanged:OnVisibleCellCountChanged);publicstaticreadonlyBindablePropertyDisableScrollProperty=BindableProperty.Create(nameof(DisableScroll),typeof(bool),typeof(CollectionView),defaultValue:false,defaultBindingMode:BindingMode.TwoWay);/// <summary>/// Force the layout of the collection view to be updated./// Useful if you changed a property that should impact the layout but the layout is not updated./// </summary>publicCommandUpdateLayout{get;internalset;}=newCommand(()=>{});publiceventEventHandler<ListLayoutChangedEventArgs> ListLayoutChanging;publicFunc<ViewCell,Task>PreRevealAnimationAsync{get;set;}publicFunc<ViewCell,Task>RevealAnimationAsync{get;set;}publicFunc<ViewCell,Task>PostRevealAnimationAsync{get;set;}/// In certain scenarios, the first scroll of the list can be smoothen/// by pre-building some views.publicintViewCacheSize{get;set;}=0;publicboolEnableDragAndDrop{get;set;}=false;publicSnapStyleSnapStyle{get;set;}=SnapStyle.None;publicintColumnCount{get;set;}=0;publicScrollSpeedScrollSpeed{get;set;}=ScrollSpeed.Normal;

Properties available with Grid and Vertical ListLayout

publicboolEnableDragAndDrop{get;set;}=false;publicstaticreadonlyBindablePropertyDragAndDropStartedCommandProperty=BindableProperty.Create(nameof(DragAndDropStartedCommand),typeof(ICommand),typeof(CollectionView));publicstaticreadonlyBindablePropertyDragAndDropEndedCommandProperty=BindableProperty.Create(nameof(DragAndDropEndedCommand),typeof(ICommand),typeof(CollectionView));publicstaticreadonlyBindablePropertyIsDragAndDroppingProperty=BindableProperty.Create(nameof(IsDragAndDropping),typeof(bool),typeof(CollectionView),defaultValue:false);

Some implementation details

Android

The Android renderer is implemented with a RecyclerView. Padding and item spacing is computed by an extension of ItemDecoration. While column computing and item distribution is achieved by a custom GridLayoutManager. The Snap to first item is implemented with a custom LinearSnapHelper. Drag and drop is handled by an ItemTouchHelper.Callback.

iOS

The iOS renderer is implemented by a UICollectionView. Padding and item spacing are natively provided by the UICollectionViewFlowLayout. Snap to Center item is brought by a little trick on DecelerationEnded callback. Drag and drop is handled by a UILongPressGestureRecognizer followed by calls to the xxxInteractiveMovementxxx methods.

About

A performant list view supporting: grid, horizontal and vertical layout, drag and drop, and reveal animations.

Resources

Stars

276 stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages