yams yams - 2 months ago 20
Android Question

Listview with Checkbox is checking more than one checkbox

So I've been working on a app with a listview with checkbox's and I've noticed when I select a item say the first item on the list when I scroll down the first item after scrolling down a full page has the checkbox selected as well. I have a feeling this is due to recyled views but I'm curious as to the solution for this. I had tried adding a viewholder but this didn't fix the issue. Just wondering what I'm missing. Code for the getview on the adapter is below:

public override Android.Views.View GetView(int position, Android.Views.View convertView, ViewGroup parent)
{
Android.Views.View view = convertView;
if (view == null)
{
view = context.LayoutInflater.Inflate(Resource.Layout.GroupTimeTrackerTemplate, parent, false);
}
tblWorkers item = this[position];

view.FindViewById<TextView>(Resource.Id.UserNameTextView).Text = this[position].nWorkerFirstname + " " + this[position].nWorkerLastname;
view.FindViewById<TextView>(Resource.Id.StatusTextView).Text = this[position].nStatusShort;
view.FindViewById<TextView>(Resource.Id.UserRoleTextView).Text = this[position].nTitle;
CheckBox chkSelect = view.FindViewById<CheckBox>(Resource.Id.ChkSelect);
chkSelect.Tag = item.nWorkerFirstname + " " + item.nWorkerLastname;

chkSelect.SetOnCheckedChangeListener(null);
chkSelect.SetOnCheckedChangeListener(new CheckChangeListener(this.context));
return view;
}

private class CheckChangeListener : Java.Lang.Object, CompoundButton.IOnCheckedChangeListener
{

private Activity activity;

public CheckChangeListener(Activity activity)
{
this.activity = activity;
}

public void OnCheckedChanged(CompoundButton buttonView, bool isChecked)
{
if (isChecked)
{
string name = (string)buttonView.Tag;
string text = string.Format("{0} Checked.", name);
Toast.MakeText(this.activity, text, ToastLength.Short).Show();
}
}
}

Answer

I ended up finding a solution that has a View Holder that works with my dynamic model data. And this worked for me below is the solution! The two main points are the business logic object having a bool to hold the item being checked and the view holder helping with performance!

   private class ViewHolderItem : Java.Lang.Object
    {
        public TextView txtName;
        public TextView txtStatus;
        public TextView txtTitle;
        public CheckBox chkItem;

        public ViewHolderItem()
        {

        }
        public ViewHolderItem(TextView textName, TextView textStatus, TextView textTitle, CheckBox checkItem)
        {
            txtName = textName;
            txtStatus = textStatus;
            txtTitle = textTitle;
            chkItem = checkItem;
        }
    }

    private ViewHolderItem holder;
    public override Android.Views.View GetView(int position, Android.Views.View convertView, ViewGroup parent)
    {

        var thisRow = list[position];
        TextView _txtName;
        TextView _txtStatus;
        TextView _txtTitle;
        CheckBox _chkItem;
        if (convertView == null)
        {
            convertView = context.LayoutInflater.Inflate(Resource.Layout.GroupTimeTrackerTemplate, null);
            _txtName = convertView.FindViewById<TextView>(Resource.Id.UserNameTextView);
            _txtStatus = convertView.FindViewById<TextView>(Resource.Id.StatusTextView);
            _txtTitle = convertView.FindViewById<TextView>(Resource.Id.UserRoleTextView);
            _chkItem = convertView.FindViewById<CheckBox>(Resource.Id.ChkSelect);
            convertView.Tag = new ViewHolderItem(_txtName, _txtStatus, _txtTitle, _chkItem);

            _chkItem.Click += (o, e) =>
            {
                var cb = (CheckBox)o;

                Vm.SelectedWorker = thisRow;
                //Vm.SelectedWorker.StatusSelected = cb.Checked;
                thisRow.StatusSelected = cb.Checked;
                //var item = (ViewHolderItem)cb.Tag;
                //item.chkItem = cb.Checked;

            };
        }
        else
        {
            var holder = (ViewHolderItem)convertView.Tag;
            _txtName = holder.txtName;
            _txtStatus = holder.txtStatus;
            _txtTitle = holder.txtTitle;
            _chkItem = holder.chkItem;
            _chkItem.Checked = thisRow.StatusSelected;
        }
        _chkItem.Checked = thisRow.StatusSelected;
        _chkItem.Focusable = false;
        _chkItem.FocusableInTouchMode = false;



        _txtName.Text = list[position].nWorkerFirstname + " " + list[position].nWorkerLastname;
        _txtStatus.Text = list[position].nStatusShort;
        _txtTitle.Text = list[position].nTitle;
        _chkItem.Checked = list[position].StatusSelected;
        return convertView;
    }