Richard Bailey
Richard Bailey

Reputation: 2738

ListView not updating after async call

I have a weird / unique situation with my ListView. This is the scenario:

I'm making use of the MVP design pattern. As the Activity starts, it raises an event to notify the presenter to fetch some data from a web service. The web service call is an Async call. Once the web service Completed event is raised, I take the result and push it into a property (which is of type Array) that resides within my View / Activity.

Everything I mentioned works just fine, but as soon as the device is rotated, some interesting developments take place.

The async call resumes as normal and provides the property (Array) with a value. So nothing wrong there... (And yes there is data in the collection) I then set the ListView Adapter and call the notifyDataSetChanged, but nothing happens. The UI is not updated or anything?? If I re-enter the Activity the data is visible again ??

I even tried calling invalidateViews and invalidate on the ListView - this didn't do anything.

Could someone please assist me in this matter? Many thanks in advance!

[Update]

I would like to stress the fact that I am making use of C# (Xamarin) and not Java (:sigh: - yes I know). Furthermore, I am not making use of the ASyncTask class, instead I'm making use of the async methods created within the proxy classes generated by Visual Studio. Pretty straight forward, but this is the code that populates the ListView - the property is set from the presenter

Presenter

Where View is of type IContactsView

    protected override void OnCollectData(System.Collections.IEnumerable data, Type typeOfData)
    {
        if (data != null && typeOfData != null && typeOfData.Equals(typeof(UserContact)))
        {
            this.View.UserInformationCollection = data.Cast<UserContact>().ToArray();
        }
    }

Activity

The activity implements IContactsView

    public UserContact[] UserInformationCollection
    {
        get
        {
            return this._userInformationCollection;
        }
        set 
        {
            this.RunOnUiThread(() =>
                {
                    this._userInformationCollection = value;                        
                    ListView listview = this.FindViewById<ListView>(Resource.Id.userLV);
                    if (listview != null)
                    {                           
                        UserContact[] subsidiesList = this.GetIndexedContacts(this._userInformationCollection);
                        listview.Adapter = new ContactsAdapter(this, subsidiesList.ToList());
                        ((ContactsAdapter)listview.Adapter).NotifyDataSetChanged();
                    }
                });
        }
    }

[/Update]

Upvotes: 1

Views: 636

Answers (2)

Richard Bailey
Richard Bailey

Reputation: 2738

Found a much better solution! So please ignore the static variable idea!

Activity:

Override the OnRetainNonConfigurationInstance and return the presenter

    public override Java.Lang.Object OnRetainNonConfigurationInstance()
    {
        return this._presenter;
    }

Within the OnCreate check the LastNonConfigurationInstance and get the presenter - if it isn't null:

    protected override void OnCreate(Bundle bundle)
    {
        ...
        if (this.LastNonConfigurationInstance != null)
        {
            this._presenter = this.LastNonConfigurationInstance as ContactsPresenter;
            this._presenter.RefreshView(this);
        }
        else
        {
            // create a new presenter
            this._presenter = new ContactsPresenter(this);
        }
        ...
    }

So maybe, you saw what I did in the previous code sample? Yes, I send the new instance of the activity to the presenter - have a look at the RefreshView

Presenter:

So within my base presenter I have the following method:

public class Presenter<T> : Java.Lang.Object, IPresenter where T : IView
{
    /// <param name="view">The view.</param>
    public void RefreshView(T view)
    {
        this.View = view;
    }
}

The above code helps my presenter say with the creation of new activities - so when it returns data after the async call it will have the latest and greatest instance of the activity!

Hope this helps! Kind regards,

Upvotes: 2

Richard Bailey
Richard Bailey

Reputation: 2738

Got it working by doing the following:

declare a static variable of the activity:

    private static ContactsActivity _cachedActivity = null;

Overrode the OnResume within the activity and set the variable:

    protected override void OnResume()
    {
        base.OnResume();
        _cachedActivity = this;
    }

Override the OnCreate within the activity and set the variable:

    protected override void OnCreate(Bundle bundle)
    {
        ...
        _cachedActivity = this;
        ...
    }

Lastly I changed the property mentioned earlier:

    public USBUserContact[] UserInformationCollection
    {
        get
        {
            return this._userInformationCollection;
        }
        set
        {
            _cachedActivity.RunOnUiThread(() =>
                {
                    _cachedActivity._userInformationCollection = value;
                    ListView listview = _cachedActivity.FindViewById<ListView>(Resource.Id.userLV);
                    if (listview != null)
                    {
                        UserContact[] subsidiesList = _cachedActivity.GetIndexedContacts(_cachedActivity._userInformationCollection);
                        listview.Adapter = new ContactsAdapter(_cachedActivity, subsidiesList.ToList());
                        ((ContactsAdapter)listview.Adapter).NotifyDataSetChanged();
                    }
                });
        }
    }

Kind regards,

Upvotes: 1

Related Questions