fstam
fstam

Reputation: 699

What steps can be taken to optimize a ListView

I have an ObservableCollection bound to a ListView. This List is basically a selection of items. If you click one, its state changes. Its added to favourites and is shown in the favourites list(also a ListView) or it gets removed from favourites.

This is the main function of my app, so there will be a lot of adding-removing going on. Both lists are slow, buggy and flicker when updated.

How can I go about making it faster/smooth?

I have tried running all add/remove calls on a worker thread. I have tried using a Task and async function (which made it worse actually). Is there a "proper" implementation of this scenario?(ListView ui thread and worker thread keeping it up to date) Is there an article that could teach me the good practise?

Implementation: note: Truck is another class which stores the data. From Data class:

List<Truck> trucks = new List<Truck>();
public void addToFavorites(Truck truck)
{
    foreach(Truck t in trucks)
    {
        if(t == truck)
        {
            t.setFavorite(true);
        }
    }
}
public void removeFromFavorites(Truck truck)
{
    foreach (Truck t in trucks)
    {
        if (t == truck)
        {
            t.setFavorite(true);
        }
    }
}
public List<Truck> getTrucks()
{
    return trucks;
}
public List<Truck> getFavoriteTrucks()
{
    List<Truck> temp = new List<Truck>();
    foreach(Truck t in trucks)
    {
        if (t.isFavorite())
        {
            temp.Add(t);
        }
    }
    return temp;
}

From the page that shows all Trucks:

public partial class AllPage : ContentPage
{
    public AllPage(csharp.Data data)
    {
        //init
        InitializeComponent();
        this.data = data;
        //build list
        refreshAsync();
        ListView list = new ListView()
        {
            ItemTemplate = new DataTemplate(typeof(csharp.PlateCell)),
            ItemsSource = trucks,
            IsPullToRefreshEnabled = true,
        };
        //on select
        list.ItemSelected += (sender, e) => {
            if (e.SelectedItem == null) return; //row deselected, dont do anything
            var selection = e.SelectedItem as csharp.Truck;
            if (selection.isFavorite())
            {
                data.removeFromFavorites(selection);
                selection.setFavorite(false);
            }
            else { 
                data.addToFavorites(selection);
                selection.setFavorite(true);
            }
            ((ListView)sender).SelectedItem = null; // de-select the row
            refreshAsync();
        };
        list.RefreshCommand = new Command(() =>
        {
            //trucks = data.getFavoriteTrucks();
            refreshAsync();
            list.IsRefreshing = false;
        });
        //add the list to the page
        root.Children.Add(list);
    }//end constructor
    csharp.Data data;
    ObservableCollection<csharp.Truck> trucks = new ObservableCollection<csharp.Truck>();

    private async Task refreshAsync()
    {
        await Task.Run(() => refreshThread());
    }

    private void refreshThread()
    {
        List<csharp.Truck> all = data.getTrucks();
        trucks.Clear();
        foreach (csharp.Truck t in all)
        {
            trucks.Add(t);
        }
    }
}

Upvotes: 1

Views: 330

Answers (2)

hvaughan3
hvaughan3

Reputation: 11105

You might want to check out this thread talking about FastCell. I have not tried his library out yet but I am planning to as I also have a list which is very shaky on Android.

I have heard that limiting the amount of items in your list at any one time can help but it never seemed to do much for me.

Also, ListView now allows you to enable ListViewCachingStrategy. RecycleElement which is supposed to help but it sounds like for you it will not currently work because you are changing the contents of your ViewCell. I ran into the same thing. When enabled, iOS seems to cache your ViewCell's initial layout so when you change the ViewCell's layout, the UI does not update. I know there is a bug report for that somewhere but not sure what it's status is.

Besides that, I would make PlateCell as simple as you possibly can. The more images and labels and layouts in your ViewCell the worse it is going to be. You could also use AbsoluteLayout in your ViewCell which, if done right, can dramatically reduce the number of layout calculations needed.

The final step that I have not been willing to make yet is to write your ViewCell in native code which I hear is much more performant than writing one in Xamarin Forms.

Let us know if something does work out for you though!

Upvotes: 0

Markus
Markus

Reputation: 215

i think a good starting point is to inform yourself about Virtualizing

You should avoid updating your observable collection in other threads than the GUI thread (look at this)

A good point would also to have a look at MVVM

As far as i can see, you will always delete your list and fill it again, instead of propagating only the changes. -> this should be your focus in order to make use of virtualisation

also you might want to have a look at this. With the SmartCollection you will only fire the CollectionChanged Event once, instead for every item you add to your list

Upvotes: 4

Related Questions