Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin Forms ListView Programatic Refresh Not Stopping on Android When Page Loaded

I have Portable project in Visual Studio 2015 with a ListView that gets populated with some data via an API call through the following function refreshData:

async Task refreshData()
{
    myListView.BeginRefresh();
    var apiCallResult = await App.Api.myApiGetCall();            
    myListView.ItemsSource = apiCallResult;
    myListView.EndRefresh();    
}

refreshData() is called in

protected override void OnAppearing()
{
    base.OnAppearing();
    refreshData();
}

Everything is working fine except on Android where the refresh indicator is not stopping or disappearing on EndRefresh() when the page is initially loaded. The page is in a TabbedPage so I can go to a different tab and then return to this page and the refresh indicator properly starts and stops with completion of my API call.

Why is refresh is not stopping when the page initially loads on Android? Any help would be appreciated.

Note: This works perfectly fine when I run on iOS.

So far I've tried:

  1. replacing myListView.BeginRefresh() with myListView.IsRefreshing = true and myListView.EndRefresh() with myListView.IsRefreshing = false

  2. Using Device.BeginInvokeOnMainThread(() => {//update list and endRefresh}).

  3. Using async void refreshData() instead of async Task refreshData().

like image 574
DerFlickschter Avatar asked Feb 10 '17 17:02

DerFlickschter


People also ask

How do I refresh a list view in Xamarin forms?

Pull to Refresh Xamarin Forms ListView control has the ability to allow the user to pull down from the top of the ListView to trigger a refresh command. When the user triggers a PullToRefresh the Command will be invoked the Refreshed event.

How do I refresh a listview in Android?

Now when you pull down from the top of the listview, it will show a refresh animation, while IsRefreshing=”true”. On each platform we can customize a small part of the refresh animation. Android uses the SwipeRefreshLayout control to display an animation while the ListView is being updated.

What are embedded resources in Xamarin forms?

Alternatively, embedded resources are also a simpler solution to distribute the data files with an app. Xamarin Forms ListView control has the ability to allow the user to pull down from the top of the ListView to trigger a refresh command.

Is my library compatible with Xamarin forms?

unfortunately your library is not compatible with Xamarin Forms unless you use ObservableCollection instead of BindingList, I wonder if this is possible ? Thanks Sir! Sorry, something went wrong.


1 Answers

Personally I can get this problem when I start ListView refreshing in the Page Contructor and stop it after the data is loaded. Sometimes (quite often) Xamarin.Forms ListView doesn't cancel refreshing animation.

I believe you faced with a quite common issue with Android SwipeRefreshLayout: it may not stop refreshing animation after setRefreshing(false) called. Native Android developers use the following approach:

swipeRefreshLayout.post(new Runnable() {
    @Override
        public void run() {
            mSwipeRefreshLayout.setRefreshing(refreshing);
    }
});

Interestingly, Xamarin.Forms uses this approach when it sets initial refreshing status (code); however, it is not enough. You need a custom renderer:

public class ExtendedListViewRenderer : ListViewRenderer
{
    /// <summary>
    /// The refresh layout that wraps the native ListView.
    /// </summary>
    private SwipeRefreshLayout _refreshLayout;

    public ExtendedListViewRenderer(Android.Content.Context context) : base(context)
    {
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _refreshLayout = null;
        }
        base.Dispose(disposing);
    }

    protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
    {
        base.OnElementChanged(e);
        _refreshLayout = (SwipeRefreshLayout)Control.Parent;
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == ListView.IsRefreshingProperty.PropertyName)
        {
            // Do not call base method: we are handling it manually
            UpdateIsRefreshing();
            return;
        }
        base.OnElementPropertyChanged(sender, e);
    }

    /// <summary>
    /// Updates SwipeRefreshLayout animation status depending on the IsRefreshing Element 
    /// property.
    /// </summary>
    protected void UpdateIsRefreshing()
    {
        // I'm afraid this method can be called after the ListViewRenderer is disposed
        // So let's create a new reference to the SwipeRefreshLayout instance
        SwipeRefreshLayout refreshLayoutInstance = _refreshLayout;

        if (refreshLayoutInstance == null)
        {
            return;
        }

        bool isRefreshing = Element.IsRefreshing;
        refreshLayoutInstance.Post(() =>
        {
            refreshLayoutInstance.Refreshing = isRefreshing;
        });
    }
}
like image 179
Mikalai Daronin Avatar answered Sep 21 '22 20:09

Mikalai Daronin