xamarin.androidlistview-adapter

Update custom listviewAdapter


I wanne add new data to my listviewAdapter, i tried it with clearing all the data before it and notifying that there is now data but it doesn't seem to work. I followed the Solution (stackoverflow) but it doesn't work.

Does anyone have an idea why?

public class ReportsListViewAdapter : BaseAdapter<IMobileReport>, IFilterable
{
    internal List<IMobileReport> originalData;
    internal List<IMobileReport> reports;
    private Context context;
    public override IMobileReport this[int position] => reports[position];

    public ReportsListViewAdapter(Context context, IEnumerable<IMobileReport> reports)
    {
        this.reports = reports.OrderBy(report => report.StudyDate).ToList();
        this.context = context;
        Filter = new ReportsFilter(this);
    }

    public override int Count => this.reports.Count;
    public Filter Filter { get; private set; }
    public override long GetItemId(int position)
    {
        return position;
    }

    public void updateReportsList(List<MobileReport> newlist)
    {
        reports.AddRange(newlist);
        this.NotifyDataSetChanged();
    }

    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        View row = convertView;
        if(row == null)
        {
            row = LayoutInflater.From(context).Inflate(Resource.Layout.listView_reports_row, null, false);
        }

        var txtName = row.FindViewById<TextView>(Resource.Id.txtName);
        txtName.Text = reports[position].Student.Name;

        var txtFirstName = row.FindViewById<TextView>(Resource.Id.txtFirstName);
        txtFirstName.Text = reports[position].Student.FirstName;

        var txtSource = row.FindViewById<TextView>(Resource.Id.txtSource);
        txtSource.Text = reports[position].Source;
        var txtCritical = row.FindViewById<TextView>(Resource.Id.txtCritical);
        txtSource.Text = reports[position].Critical.ToString();
        return row;

    }
}


protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        reports = new List<IMobileReport>();
        //Init();
        _reportsHubConnector = new ReportsHubConnector();

        #pragma warning disable 4014 // We cannot await this task here because the signature of the inherited method is void
        Task.Factory.StartNew(async () =>
        {
            await _reportsHubConnector.StartConnection();
            await _reportsHubConnector.SendT();

        }, TaskCreationOptions.PreferFairness);
        #pragma warning restore 4014

        Console.WriteLine("HomeActivity: OnCreate");
        SetContentView(Resource.Layout.activity_reports);
        SupportActionBar.SetDisplayShowTitleEnabled(false);
        SupportActionBar.SetDisplayHomeAsUpEnabled(false);
        SupportActionBar.SetDisplayShowHomeEnabled(true);

        WireUpElements();

        listView = FindViewById<ListView>(Resource.Id.reports);
        ReportsListViewAdapter adapter = new ReportsListViewAdapter(this, reports);

        listView.Adapter = adapter;

        searchView = FindViewById<SearchView>(Resource.Id.searchView1);
        searchView.QueryTextChange += this.Filter;

        listView.ItemClick += ItemClicked;

        criticalButton = FindViewById<LinearLayout>(Resource.Id.AuthenticatorButton);
        criticalButton.Click += criticalClicked;

        _reportsHubConnector.ReportsRecieved += (reports) =>
        {
            adapter.updateReportsList(reports);

        };
    }

When i debug slowly the GetView does get triggered, maybe this is a clue to why its not being called when i don't debug or go over the code quick.

This.RunOnUiThread is never called but update is.

 _reportsHubConnector.ReportsRecieved += (tmpReports) =>
 {
       adapter.updateReportsList(tmpReports);
       this.RunOnUiThread(() =>
       {

            criticalButton.SetBackgroundColor(Android.Graphics.Color.Red);
        });
  };

Solution

  • i tried it with clearing all the data before it and notifying that there is now data but it doesn't seem to work.

    From shared code , not seeing clear method , you can add reports.Clear() to check whether it works .

    public void updateReportsList(List<MobileReport> newlist)
    {
        reports.Clear();
        reports.AddRange(newlist);
        this.NotifyDataSetChanged();
    }
    

    If not works , need to check whehter added newlist is the correct data format.

    ========================Update========================

    In OnCreate method , where _reportsHubConnector.ReportsRecieved call update method modify as follow :

    _reportsHubConnector.ReportsRecieved += (tmpReports) =>
    {
        adapter.updateReportsList(tmpReports);
    
    };
    

    Change reports argument to tmpReports to avoid mixing with the original data reports .

    Therefore , there is another common way to update data of adapter as follow :

    _reportsHubConnector.ReportsRecieved += (reports) =>
    {
        List<IMobileReport> tmp = new List<IMobileReport>();
        tmp =  reports ; // use a tmp list data for updating , not using original list data 
        adapter.updateReportsList(tmp);
    
    };
    

    ============================Update===============================

    From my sample project , I find a interesting phenomenon and that maybe the problem.

    Here I will share my custom adapter HomeScreenAdapter :

    public class HomeScreenAdapter : BaseAdapter<TableItem> {
        List<TableItem> items;
        Activity context;
        public HomeScreenAdapter(Activity context, List<TableItem> items)
            : base()
        {
            this.context = context;
            this.items = new List<TableItem>();
            this.items.AddRange(items);
            //this.items = items;
        }
        public override long GetItemId(int position)
        {
            return position;
        }
        public override TableItem this[int position]
        {
            get { return items[position]; }
        }
        public override int Count
        {
            get { return items.Count; }
        }
        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            var item = items[position];
    
            View view = convertView;
            if (view == null) // no view to re-use, create new
                view = context.LayoutInflater.Inflate(Resource.Layout.CustomView, null);
            view.FindViewById<TextView>(Resource.Id.Text1).Text = item.Heading;
            view.FindViewById<TextView>(Resource.Id.Text2).Text = item.SubHeading+" items";
            view.FindViewById<ImageView>(Resource.Id.Image).SetImageResource(item.ImageResourceId);
    
            return view;
        }
    
        public void UpdateListView(List<TableItem> newTableItem)
        {
            items.Clear();
            items.AddRange(newTableItem);
            NotifyDataSetChanged();
        }
    }
    

    You will see in the Constructor of HomeScreenAdapter , I commented this line of code this.items = items; . The reault that it will work:

    enter image description here

    However , if I use this.items = items; to replace this.items.AddRange(items);, it will update nothing even can not show anything in ListView .

    public HomeScreenAdapter(Activity context, List<TableItem> items)
        : base()
    {
        this.context = context;
        this.items = new List<TableItem>();
        //this.items.AddRange(items);
        this.items = items;
    }
    

    The effect :

    enter image description here

    The possible reason :

    If the equal sign is used here, the pointer address of the items will change. When the data is updated, it cannot point to the original data source, so the update cannot be successful.

    Therefore , here Constructor of your code can modify as follow to check whehter it works :

    public ReportsListViewAdapter(Context context, IEnumerable<IMobileReport> reports)
    {
        //this.reports = reports.OrderBy(report => report.StudyDate).ToList();
        this.reports =  new List<IMobileReport>();
        this.reports.AddRange(reports.OrderBy(report => report.StudyDate).ToList());
        this.context = context;
        Filter = new ReportsFilter(this);
    }
    

    Related : Here is the sample project link .