Search code examples
androidxamarinxamarin.androidbaseadapter

BaseAdapter Changing Values while scrolling


I'm implementing a listview that where each element contains 2 textviews and a seekbar. Layout like this (this): Layout

The activity has a viewpager with 3 different fragments. In fragment 3 i use a listview to display a custom adapter. the number of items in the list is dynamic so it can be possible that scrollbars appear.

If scrollbars are present and one scrolls some views out of display and back in again the values sometimes change. I know this is due to the recreation in GetView() in the adapter. But I can't find the reason why it changes.

The process should be like this:

  • Nothing select: seekbar is gray and textview on the right shows not initialized
  • Something selected: bar is blue and right textview seekbar progress

Here is the code of the Custom Adapter:

class EntryAdapter : BaseAdapter<Entry>
{
    List<Entry> mEntryList;
    Activity mActivity;
    ColorFilter grayFilter;
    ColorFilter blueFilter;

    public EntryAdapter(Activity activity, List<Entry> entryList) : base()
    {
        mEntryList = entryList;
        mActivity = activity;

        grayFilter = new PorterDuffColorFilter(Color.Gray, PorterDuff.Mode.SrcIn);
        blueFilter = new PorterDuffColorFilter(Color.Blue, PorterDuff.Mode.SrcIn);
    }

    public override Entry this[int position]
    {
        get
        {
            return mEntryList[position];
        }
    }

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

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

    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        ViewHolder holder = null;
        View view = convertView;

        if (view == null)
        {
            holder = new ViewHolder();
            view = mActivity.LayoutInflater.Inflate(Resource.Layout.ListItem_Entry, null);
            holder.Name = view.FindViewById<TextView>(Resource.Id.li_entry_tv_feature_name);
            holder.Value = view.FindViewById<TextView>(Resource.Id.li_entry_tv_feature_value);
            holder.Unit = view.FindViewById<TextView>(Resource.Id.li_entry_tv_feature_value_unit);
            holder.Seeker = view.FindViewById<SeekBar>(Resource.Id.li_entry_sb_feature);
            view.Tag = holder;
        }

        holder = (ViewHolder)view.Tag;

        Entry current = mEntryList[position];


        holder.Seeker.Max = current.Max + 1;
        holder.Seeker.Progress = current.Value;
        holder.Seeker.Tag = current;
        holder.Name.Text = current.Name;
        holder.Value.Text = Convert.ToString(current.Value);     
        holder.Seeker.ProgressChanged += delegate (object sender, SeekBar.ProgressChangedEventArgs e) 
        {
            Entry entry = e.SeekBar.Tag as Entry;

            if (e.Progress <= 0)
            {
                holder.Name.Text = entry.Name;
                entry.Value = 0;
            }
            else
            {
                holder.Name.Text = entry.Name + " (" + entry.Unit + "):";
                entry.Value = e.Progress - 1;
            }

            if (e.Progress <= 0)
            {
                holder.Value.Text = "Not initialized!";
                holder.Seeker.ProgressDrawable.SetColorFilter(grayFilter);
                holder.Seeker.Thumb.SetColorFilter(grayFilter);
            }
            else
            {
                holder.Value.Text = Convert.ToString(e.Progress - 1);
                holder.Seeker.ProgressDrawable.SetColorFilter(blueFilter);
                holder.Seeker.Thumb.SetColorFilter(blueFilter);
            }
        };

        if (holder.Seeker.Progress <= 0)
        {
            holder.Name.Text = mEntryList[position].Name;
            mEntryList[position].Value = 0;
        }
        else
        {
            holder.Name.Text = mEntryList[position].Name + " (" + mEntryList[position].Unit + "):";
            mEntryList[position].Value = holder.Seeker.Progress;
        }

        if (holder.Seeker.Progress <= 0)
        {
            holder.Value.Text = "Not initialized!";
            holder.Seeker.ProgressDrawable.SetColorFilter(grayFilter);
            holder.Seeker.Thumb.SetColorFilter(grayFilter);
        }
        else
        {
            holder.Value.Text = Convert.ToString(holder.Seeker.Progress);
            holder.Seeker.ProgressDrawable.SetColorFilter(blueFilter);
            holder.Seeker.Thumb.SetColorFilter(blueFilter);
        }

        return view;
    }

    public class ViewHolder : Java.Lang.Object
    {
        public TextView Name { set; get; }
        public TextView Value { set; get; }
        public SeekBar Seeker { set; get; }
        public Entry CurrentEntry { set; get; }
    }
}

Class Entry looks like this:

class Entry : Java.Lang.Object, IParcelable
{
    [ExportField ("CREATOR")]
    static EntryCreator InitialieCreator()
    {
        return new EntryCreator();
    }


    public int Id { set; get; }
    public int Max { set; get; }
    public int Value { set; get; }
    public string Name { set; get; }

    public Entry()
    {

    }

    public Entry (int id, int max, int value, string name)
    {
        Id = id;
        Max = max;
        Value = value;
        Name = name;
    }

    public int DescribeContents()
    {
        return 0;
    }

    public void WriteToParcel(Parcel dest, [GeneratedEnum] ParcelableWriteFlags flags)
    {
        dest.WriteInt(Id);
        dest.WriteInt(Max);
        dest.WriteInt(Value);
        dest.WriteString(Name);
    }

    public class EntryCreator : Java.Lang.Object, IParcelableCreator
    {
        public Java.Lang.Object CreateFromParcel(Parcel source)
        {
            return new Entry(source.ReadInt(), source.ReadInt(), source.ReadInt(), source.ReadString());
        }

        public Java.Lang.Object[] NewArray(int size)
        {
            return new Java.Lang.Object[size];
        }
    }
}

Maybe someone can tell me why the values go crazy. Ranging from "not initialized" to n everything is possible while scrolling. I'm at my wits end sitting on this for several weeks now.


Solution

  • I finally found out why this is happening, due to having the same problem with EditText widgets.

    The reason for it is that the altering of the values that happens while the app initializes the views also triggers the listeners. Sometimes it isn't fast enough sometimes it jumbles the values with a wrong position.

    To stop that the SeekBar.ProgressChangedEventArgs offers the bool FromUser which shows if the change was made by user or programmatically. So I just wrapped up all changes in the listener with an if(FromUser).

    Incidentially the TextWatcher for EditTexts does not offer this parameter. But in this answer I found a working solution. If you don't need the Tag of the EditText.

    In short: fill the Tag with random stuff befor you alter the EditText from code and set the Tag NULL afterwards. In the listener check if Tag is NULL (user edit) or NOT NULL (programmatically).