Reputation: 699
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
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
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