带复选框的列表视图正在选中多个复选框

Listview with Checkbox is checking more than one checkbox

所以我一直在开发一个带有复选框的列表视图的应用程序,我注意到当我 select 向下滚动后向下滚动第一个项目时,一个项目说列表中的第一个项目整页也有 select 复选框。我有一种感觉,这是由于回收的观点,但我很好奇对此的解决方案。我曾尝试添加一个取景器,但这并没有解决问题。只是想知道我错过了什么。适配器上的 getview 代码如下:

        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();
                }
            }
        }

虽然我个人没有通过 Xamarin 开发 Android,但我有 Android 知识可以帮助您朝着正确的方向前进。

如您正确所述,ListView 中的行被回收。问题是,当您滚动时,会调用 OnCheckedChange 方法,并且由于它回收了行,因此它在未选择的新行的位置传递,但由于它与先前选择的索引具有相同的索引改变它的价值。

例如,如果您选中索引为 5 的框,然后向下滚动,新行将变为索引为 5 的行,因此它会更改其状态。

这是 Android 上的常见问题。要解决它,首先你需要一个数据结构来跟踪哪些行已经被检查过,方法是将它存储在地图或列表中。

然后在您的 GetView 中确保将复选框状态设置为该位置的内容。

@Ivan Alburquerque 是对的,因为 Android 回收了 list.You 中的视图,应该始终根据您的位置设置 GetView 方法上复选框的值数据源。 为了避免您在这里编写的所有这些重复代码,我建议您使用像 MvvmCross (https://github.com/mvvmcross/mvvmcross) 这样的 MVVM 框架,它可以为您完成所有工作,并且您需要编写的代码会少很多. 玩得开心。

我最终找到了一个解决方案,它有一个 View Holder,可以处理我的动态模型数据。这对我有用,下面是解决方案!三个要点是业务逻辑对象有一个 bool 来保存被检查的项目和视图持有者帮助提高性能,最后因为视图的回收你不能在点击事件中可靠地使用列表 [位置] 所以我用了包装纸!

视图持有人:

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

    }

适配器获取视图:

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

        var item = list[position];
        Android.Views.View view = convertView;
        MyViewHolder holder;
        if (convertView == null)
        {
            holder = new MyViewHolder();
            view = context.LayoutInflater.Inflate(Resource.Layout.GroupTimeTrackerTemplate, null);
            holder.chkItem = view.FindViewById<CheckBox>(Resource.Id.ChkSelect);
            holder.txtName = view.FindViewById<TextView>(Resource.Id.UserNameTextView);
            holder.txtStatus = view.FindViewById<TextView>(Resource.Id.StatusTextView);
            holder.txtTitle = view.FindViewById<TextView>(Resource.Id.UserRoleTextView);
            view.Tag = holder;
        }
        else
        {
            holder = view.Tag as MyViewHolder;

        }
        holder.chkItem.Checked = item.StatusSelected;
        if (holder.chkItem.Checked)
        {
            holder.chkItem.Enabled = false;
        }
        else
        {
            holder.chkItem.Enabled = true;
        }
        holder.chkItem.Tag = new MyWrapper<int>(position);
        holder.txtName.Text = item.nWorkerFirstname + " " + item.nWorkerLastname;
        holder.txtStatus.Text = item.nStatusShort;
        holder.txtTitle.Text = item.nTitle;
        holder.chkItem.Click -= HolderChkItemClick;
        holder.chkItem.Click += HolderChkItemClick;
        if (statoChk[position] == 0)
        {
            holder.chkItem.Checked = false;
        }
        else
        {
            holder.chkItem.Checked = true;
        }


        return view;
    }

复选框、包装器和数组的单击事件:

    tblWorkers OrigSel = null;
    void HolderChkItemClick(object sender, EventArgs e)
    {
        var chk = sender as CheckBox;
        int pos = ((MyWrapper<int>)chk.Tag).value;
        tblWorkers selectedWorker = list[pos];
        if (Vm.SelectedWorker == null)
        {
            OrigSel = selectedWorker;
            IntentManager.Instance.OriginalSelectedWorker = OrigSel;
            Vm.GroupTimeTrackerStatusType = OrigSel.nStatusShort;
        }
        Vm.SelectedWorker = selectedWorker;
        if (chk.Checked)
        {
            if (selectedWorker.nStatusShort == OrigSel.nStatusShort)
            {
                statoChk[pos] = 1;
                Vm.GroupTimeList.Add(selectedWorker);
            }
            else
            {
                statoChk[pos] = 0;
                chk.Checked = false;
                Toast.MakeText(context, "Please Select Workers with the Same Time Status. Current Status : " + OrigSel.nStatusShort, ToastLength.Long).Show();
            }
        }
        else
        {
            statoChk[pos] = 0;
            Vm.GroupTimeList.Remove(selectedWorker);
            if (Vm.GroupTimeList.Count == 0)
            {
                Vm.SelectedWorker = null;
                OrigSel = null;
                IntentManager.Instance.OriginalSelectedWorker = null;
                Vm.GroupTimeTrackerStatusType = "";
            }
        }


    }

    public class MyWrapper<T> : Java.Lang.Object
    {
        private T _value;
        public MyWrapper(T managedValue)
        {
            _value = managedValue;
        }
        public T value { get { return _value; } }
    }


    int[] statoChk;
    public int[] StatoCheck
    {
        get { return statoChk; }
        set { statoChk = value; }
    }