Xamarin.Forms ultimate horizontal list guide

HorizontalListImate

Hey Developers, happy new year! Let’s talk about UI components today! I have been playing around with custom controls, custom renders and other tools to try to get to the best solution for Cross Platform Horizontal Lists with Xamarin Forms! I reached a few solutions and Would like to share them with you!

If you are in a hurry and really can’t stick around to read the rest of the post, well,  here is the link for the Sample project in Github, clone it, fork it, try it out and be happy!

Like some other UI components in Xamarin Forms, listviews that scroll horizontally are not right out of the box for us to use. In this post, I decided to bring to you a few ways to  implement this control, let’s list them bellow:

  1. Rotated Xamarin Forms ListView.
  2. Custom Control – Grid + Horizontal ScrollView
  3. Custom Renders – Android.RecyclerView & iOS.UICollectionView

1. Rotated Xamarin Forms ListView

Special thanks for the help with this sample to my fellow member of the monkey nights community and fan of DDD @jbalivo.

On the MainPage.xaml of the sample app this is the first List from the top down. As the  title already says, this one is a ListView Rotated 270° with the ContentView of the ItemTemplate also rotated 90°, all inside a RelativeLayout using it’s constraints to keep everything together.

Pros:

  • Just like using a Forms ListView.
  • CachingStrategy avaiable to use.

Cons:

  • The scroll bar is sticked to the top of the listview.
  • I personally do not like dealing with RelativeLayout and it’s constraints.
  • Your ItemTemplate HAS TO BE SQUARE, unfortunately there is this blocker. I tried messing with it but the more you poke it, the more it gets angry.

2. Custom Control – Grid + Horizontal ScrollView

I came across this amazing sample in the Hotel 360 sample app that was built for Microsoft Connect(); 2017. You can find the Hotel 360 code here, I highly recommend you to take a look at that repo and check out what those guys did, it is awesome.

On the MainPage.xaml of our sample app this will be the second ListView from the top down also known as middle. You can also check out the UI control code here, this puppy was completely copied from the Hotel 360 app so it is already wrapped with all sorts of properties for us to use.

Pros:

  • The scroll bar is on the bottom of the control.
  • You can have whatever shape you want for the ItemTemplate.

Cons:

  • Unlike normal ListViews this puppy does not have CachingStrategy available, it means that all the items in the list you are binding are loaded in memory at the same time. This can become an issue in your app if you load a lot of items, so be very careful when using this!

3. Custom Renders – Android.RecyclerView & iOS.UICollectionView

As usual, when we ran out of options we can always delegate the task to the target platforms! In this case I used Android native RecyclerView and iOS native UICollectionView components, these are really powerful components for Android and iOS platforms, I recommend you to read the documentation and see what other awesome layouts we can build with them.

On the MainPage.xaml the third ListView counting from the top down is where we call this render, you can also find the . As of the publishing of this post, the renders are only loading the list of monkeys in the screen, I did not yet setup the list update, item select command or other common features that we have in ListViews, I will be adding features during February BUT if you feel like contributing here is the link for the Gitrepo issues list, I will be more than happy and honored to accept your pull requests!

Pros:

  • Cell Recycling is available on both components.
  • The cell can have the shape that you need/wish.

Cons:

  • I don’t know if it was just me but I spend a whole weekend to set this puppies up, it is quite a lot of code that you have to write to get it up and running.

CAUSER TIP: 

Like everything in Tech, there are no silver bullets to solve our problems, here we have three ways of solving the same issue, each of the solutions are a fit based on your app needs. Choose wisely.

throw new CauserException();

References

 

15 thoughts on “Xamarin.Forms ultimate horizontal list guide

  1. Good job M. Causer 🙂
    However, I took a look at your ios implementation and:
    * Since all the inner widgets of the cells are re-render each time, I don’t know if the cell recycling bring much here…
    * Worst, since you’re not caching the rendered view, there will more than N view renderers for N cells (in fact there will a new inner view created for each time a cell is displayed).
    So, careful with the code guys, take it as a great start to create a good horizontal list, I wouldn’t use it “as is”.
    I’m sorry to bring that up cause, it’s really a great job so far 😉

    Liked by 1 person

    1. Hey man! Thank you for sharing your thoughts.
      Indeed I had to make the inner views of the cell re-render when they show in the screen, because I wanted to have this list as generic as possible, so we could easily change the cell ui structure in the forms xaml, and it would be easily rendered in the iOS project!
      Even thought we are re-rendering the view, the cells are not all rendered at the same time, so if we have a list with size N, only the items about to show in the screen would be re-rendered!
      I really don’t see a way of keeping the iOS sample generic and update the cell views! Would you like to chat about it? I would love if you could help out and make this code even more awesome! What do you say!?

      Like

      1. Hey !
        So to go a bit further, you should create a cellViewCache just to reuse the already created ViewCell, cause otherwise a new ViewCell will be created at each call of GetCell:

        public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
        {
        var nativeCell = (iOSViewCell)collectionView.DequeueReusableCell(nameof(iOSViewCell), indexPath);
        var dataContext = _dataSource[indexPath.Row];
        if (dataContext != null)
        {
        ViewCell viewCell = CreateViewCell(indexPath.Row, _view.ItemTemplate);
        nativeCell.UpdateUi(viewCell, dataContext, _view, ((UICollectionViewFlowLayout)collectionView.CollectionViewLayout).ItemSize);
        }

        return nativeCell;
        }

        private ViewCell CreateViewCell(int index, DataTemplate dataTemplate)
        {
        var cachedCell = _viewCellCache[index].CachedCell;
        if (cachedCell == null)
        {
        if (dataTemplate is DataTemplateSelector selector)
        {
        var template = selector.SelectTemplate(_dataSource[index], _view.Parent);
        cachedCell = template.CreateContent() as ViewCell;
        }
        else
        {
        cachedCell = dataTemplate?.CreateContent() as ViewCell;
        }

        _viewCellCache[index].CachedCell = cachedCell;
        }

        return cachedCell;
        }

        Of course you need to keep the _viewCellCache inline with your data source with INotifyCollectionChanged. Sorry for the chat but I’m pretty busy :/ I wanted to make a PR in the first place, but my implementation differs too much from yours now and we are so late in our goals x)

        Liked by 1 person

      2. Let me take a look at it. If you want you are more than welcome to create a PR with this fix!!!

        About keeping track of the changes on the collection I’m completely aware of that, I actualy created a couple of enhancement issues on the Repo, to add behavior to this list, and the community to help out!

        Send me a PM on twitter so we can chat more!

        Liked by 1 person

    2. I tried using the HorizontalViewNative in my project but both the iOS and Android apps crash when scrolling too much. I tried adding your cache changes but that didn’t seem to help. Have you made any more changes to get this to work?

      Like

      1. I have two lists on one page, both HorizontalViewNative and building for iOS and Android. One list is 48 items, the other is 32 items long. Each cell is is a contentView that contains an AbsoluteLayout that contains two images and a label. I posted issues on the gitHub project page about slight changes I had to make to even get the lists to show up. I don’t know if those changes are causing the crash or if it’s something else.

        Like

      1. It actually turned out to be a strange bug where my ViewModel was being called from different pages and clearing out the CollectionChanged event causing it to never be initialized for the ContentPage it was being used in. Maybe this will help someone who is having the same issue.

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s