Taylor Taylor - 3 months ago 15
Android Question

Custom ListView is changing its data when it is scrolled off screen then back

So I have a Custom ListView I made for Xamarin-Android that takes data from a list of employee shifts and turns them into rows. Some of the data is being reported incorrectly right away, (showing shifts they dont have), and when scrolling around the data is changing periodically.

Essentially I pass the adapter a List of Employees, each having a List type that holds a start and end time. The adapter loops through each shift, uses a switch to route whatever day it is for to the right TextViews, then pastes the data. For some reason the data is not displaying correctly for some of the rows, but others are fine. And when you click into the row (I have a separate view for editing the data) the data comes up correctly reflecting what it is supposed to. I have tried to step through and figure out what is going on but when stepping it is showing that the rows are being returned correctly.

On top of the data not displaying correctly, when scrolling up and down, when a row goes off screen it is sometimes coming back with different data.

Here is the adapter code:

using System;
using System.Collections.Generic;
using Android.Content;
using Android.Views;
using Android.Widget;

namespace MaydSchedulerApp
{
class ScheduleAdapter : BaseAdapter<EmployeeScheduleWrapper>
{
private List<EmployeeScheduleWrapper> mItems;
private Context mContext;
private bool sun = false, mon = false, tue = false, wed = false, thu = false, fri = false, sat = false;

public ScheduleAdapter(Context context, List<EmployeeScheduleWrapper> items)
{
mItems = items;
mContext = context;
}

public override int Count
{
get
{
return mItems.Count;
}
}

public override long GetItemId(int position)
{
return position;
}

public override EmployeeScheduleWrapper this[int position]
{
get
{
return mItems[position];
}
}

public override View GetView(int position, View convertView, ViewGroup parent)
{
View row = convertView;

if (row == null)
{
row = LayoutInflater.From(mContext).Inflate(Resource.Layout.ScheduleListView, null, false);
}

TextView txtName = row.FindViewById<TextView>(Resource.Id.txtScheduleName);
txtName.Text = mItems[position].lName + ", " + mItems[position].fName;

TextView txtPosition = row.FindViewById<TextView>(Resource.Id.txtSchedulePosition);
txtPosition.Text = CoreSystem.GetPositionName(mItems[position].position);

TextView txtSunday = row.FindViewById<TextView>(Resource.Id.txtSun);
TextView txtMonday = row.FindViewById<TextView>(Resource.Id.txtMon);
TextView txtTuesday = row.FindViewById<TextView>(Resource.Id.txtTue);
TextView txtWednesday = row.FindViewById<TextView>(Resource.Id.txtWed);
TextView txtThursday = row.FindViewById<TextView>(Resource.Id.txtThu);
TextView txtFriday = row.FindViewById<TextView>(Resource.Id.txtFri);
TextView txtSaturday = row.FindViewById<TextView>(Resource.Id.txtSat);
for (int i = 0; i < mItems[position].shiftList.Count; i++)
{
switch (mItems[position].shiftList[i].date)
{
case DayOfWeek.Sunday:
txtSunday.Text = mItems[position].shiftList[i].startShift.ToString() + "00 - " + mItems[position].shiftList[i].endShift.ToString() + "00";
sun = true;
break;
case DayOfWeek.Monday:
txtMonday.Text = mItems[position].shiftList[i].startShift.ToString() + "00 - " + mItems[position].shiftList[i].endShift.ToString() + "00";
mon = true;
break;
case DayOfWeek.Tuesday:
txtTuesday.Text = mItems[position].shiftList[i].startShift.ToString() + "00 - " + mItems[position].shiftList[i].endShift.ToString() + "00";
tue = true;
break;
case DayOfWeek.Wednesday:
txtWednesday.Text = mItems[position].shiftList[i].startShift.ToString() + "00 - " + mItems[position].shiftList[i].endShift.ToString() + "00";
wed = true;
break;
case DayOfWeek.Thursday:
txtThursday.Text = mItems[position].shiftList[i].startShift.ToString() + "00 - " + mItems[position].shiftList[i].endShift.ToString() + "00";
thu = true;
break;
case DayOfWeek.Friday:
txtFriday.Text = mItems[position].shiftList[i].startShift.ToString() + "00 - " + mItems[position].shiftList[i].endShift.ToString() + "00";
fri = true;
break;
case DayOfWeek.Saturday:
txtSaturday.Text = mItems[position].shiftList[i].startShift.ToString() + "00 - " + mItems[position].shiftList[i].endShift.ToString() + "00";
sat = true;
break;
}
}
if (!sun)
txtSunday.Text = "Off";
if (!mon)
txtMonday.Text = "Off";
if (!tue)
txtTuesday.Text = "Off";
if (!wed)
txtWednesday.Text = "Off";
if (!thu)
txtThursday.Text = "Off";
if (!fri)
txtFriday.Text = "Off";
if (!sat)
txtSaturday.Text = "Off";
return row;
}
}
}


And here is the code for the EmployeeScheduleWrapper

using System;
using System.Collections;
using System.Collections.Generic;

namespace MaydSchedulerApp
{
public class EmployeeScheduleWrapper
{
public string lName, fName;
public int employee;
public int position;
public int hourTarget;
public int skill;//This is to minimize callbacks to the main emp later
public int scheduledHours;
//The availability is copied onto this so that it can be modified without effecting the set availability in the employee type
public Availability availability = new Availability();
public bool availabilityModified = false;
public List<Shift> shiftList = new List<Shift>();

public EmployeeScheduleWrapper() { }

public EmployeeScheduleWrapper(Employee emp)
{
lName = emp.empLastName;
fName = emp.empFirstName;
employee = emp.empID;
position = emp.position;
skill = emp.skillLevel;
hourTarget = emp.hourTarget;
scheduledHours = 0;
//availability = emp.availability;//Dont init this unless its changed and needs to be saved
availabilityModified = false;
}

public void SetTempAvailability(Availability avail)//TODO add check for when avail last changed to warn user if availability is no longer useful
{
availability = avail;
availabilityModified = true;
}

public void PermanentAvailabilityChange(Availability avail)
{
availability = avail;
availabilityModified = false;//since were using the permanent change
EmployeeStorage.GetEmployee(employee).availability = avail;
}

public Availability GetEntireAvail()
{
if (availabilityModified)
return availability;
else
{//This fixes the direct memory interaction when modifying temp availability
availability = new Availability(EmployeeStorage.GetEmployee(employee).availability);
return availability;
}
}

public bool GetAvailability(int day)
{
if (availabilityModified)
{
switch (day)
{
case 0:
return availability.sunday.available;
case 1:
return availability.monday.available;
case 2:
return availability.tuesday.available;
case 3:
return availability.wednesday.available;
case 4:
return availability.thursday.available;
case 5:
return availability.friday.available;
case 6:
return availability.saturday.available;
default:
Console.WriteLine("Invalid case chosen! :: EmployeeManagement/Employee.cs :: GetAvailability(int day): Invalid Value for Day Thrown! :: Returning false!");
break;
}
return false;
}
else
{
return EmployeeStorage.GetEmployee(employee).GetAvailability(day);
}
}
}
}

Answer

You need to reset the view to empty data.

That is, the list view will re use the convertview to optimise memory. During that time, the listview will give back the previously used convert view which contains previous data. So you need to reset the data in the getView()

That is :

            txtSunday.Text = "";

            txtMonday.Text = "";

            txtTuesday.Text = "";

            txtWednesday.Text = "";

            txtThursday.Text = "";

            txtFriday.Text = "";

            txtSaturday.Text = "";

after the switch case statement

Comments