【问题标题】:ListView adapter with many checkboxes带有许多复选框的 ListView 适配器
【发布时间】:2012-04-24 13:01:19
【问题描述】:

我的列表视图有一些小问题。每个项目都有一些信息和一个复选框。这显示得很好,我可以选择和取消选择复选框等。

但是我发现了一些奇怪的行为。假设我单击第一行的第一个复选框。如果 ListView 很小,那么您不需要向下滚动,这可以正常工作。但是如果 ListView 很大,所以我需要向下滚动才能查看所有项目,底部的一些随机项目也会被点击。 相反的行为相同,如果我单击列表视图底部的复选框并向上滚动,则会在顶部单击一些随机复选框。如果我在某处单击多个复选框,则在其他地方单击的复选框数量相同。我认为这发生在调用 GetView(...) 时,即更新时。然后它会点击一些新的复选框,但我不知道为什么。

在这里感谢一些帮助! :)

我的适配器的 SSSCE:

public class ListViewAdapterSSCCE : BaseAdapter
{
    private Activity myActivity;
    private string[] someData;
    private int numberOfElements;
    private CheckBox[] boxes;

    public ListViewAdapterSSCCE(Activity activity)
    {
        this.myActivity = activity;

        for (int i = 0; i < numberOfElements; i++)
            this.boxes[i] = new CheckBox(myActivity);
    }

    public void SetData(string[] someData)
    {
        this.someData = someData;
        this.numberOfElements = someData.Length;
    }

    public void CheckAllBoxes(bool isChecked)
    {
        for (int i = 0; i < numberOfElements; i++)
            this.boxes[i].Checked = isChecked;
    }

    public override Java.Lang.Object GetItem(int position)
    {
        return null;
    }

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

    public override int Count
    {
        get { return numberOfElements; }
    }

    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        if (convertView == null)
        {
            LayoutInflater inflater = (LayoutInflater)myActivity.GetSystemService(Context.LayoutInflaterService);
            convertView = inflater.Inflate(Resource.Layout.ChildrenList_item, null);
        }

        var itemBox = convertView.FindViewById<CheckBox>(Resource.Id.checkbox);
        this.boxes[position] = itemBox;

        var textView = convertView.FindViewById<TextView>(Resource.Id.item_data);
        textView.Text = this.someData[position];

        return convertView;
    }
}

【问题讨论】:

    标签: android listview checkbox xamarin.android listview-adapter


    【解决方案1】:

    如前所述,问题在于您没有在 getView 中设置复选框值。

    您还可以大大提高代码的效率。您应该做的第一件事是将复选框添加到您的子视图 xml,而不是创建它们的数组。而是创建一个布尔数组来存储该值。

    请注意,我将像对 Java 一样修改此内容,因此您可能需要在 C# 中更改一些内容。

     public class ListViewAdapterSSCCE : BaseAdapter
        {
        private string[] someData;
        private int numberOfElements;
        private boolean[] checkValue;
        private LayoutInflater inflater;
    
        public ListViewAdapterSSCCE(Context context)
        {
    
            inflater = LayoutInflater.from(context);
        }
    
        public void SetData(string[] someData)
        {
            this.someData = someData;
            this.numberOfElements = someData.Length;
        }
    
        public void CheckAllBoxes(bool isChecked)
        {
            for (int i = 0; i < numberOfElements; i++)
                checkValue[i] = true;
        }
    
        public override Java.Lang.Object GetItem(int position)
        {
            return null;
        }
    
        public override long GetItemId(int position)
        {
            return 10;
        }
    
        public override int Count
        {
            get { return numberOfElements; }
        }
    
        public override View GetView(final int position, View convertView, ViewGroup parent)
        {
    
            final ViewHolder holder; 
    
            if (convertView == null)
            {            
                convertView = inflater.Inflate(Resource.Layout.ChildrenList_item, null);
    
                holder = new ViewHolder();
                holder.itemBox = convertView.FindViewById<CheckBox>(Resource.Id.checkbox);
                holder.textView = convertView.FindViewById<TextView>(Resource.Id.item_data);
    
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
    
            itemBox = checkValue[position];    
    
            itemBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){
    
                @Override
                public void onCheckedChanged(CompoundButton buttonView,
                        boolean isChecked) {
    
                            checkValue[positon] = isChecked;
    
                }
    
            });
    
            textView.Text = this.someData[position];
    
            return convertView;
        }
    
        class ViewHolder
        {
            CheckBox itemBox;
            TextView textView; 
        }
    }
    

    【讨论】:

    • 看起来很棒!在编辑之前看到 archanos 的帖子后,我自己也在想类似的东西,但你把它写在纸上,非常感谢!
    • 其实经过一些测试这种方法是不合法的。问题是当复选框滚动到视线之外时调用 onCheckedChanged 。因此它的状态不会被保存。我选择使用 OnClick 代替,如 Joey 回答中的链接所示。
    【解决方案2】:

    您可以在此处查看示例代码以对复选框状态进行替代修复:

    custom checkbox difficulty in android

    如果这篇文章对你有帮助,请将这篇文章标记为答案。

    谢谢。

    【讨论】:

      【解决方案3】:

      问题可能与您在适配器中处理回收视图的方式有关。查看您的getView 并检查您返回的每个项目的 CheckBox 是否根据其“模型”正确设置。

      编辑:我确认。您必须像处理 textview 的文本一样设置复选框状态。 顺便说一句,我认为以您的方式处理复选框是一个坏主意。您可以做的最好的事情是为复选框定义一个“模型”(即:一个简单的 boolean[] 可以做到这一点),避免实例化太多的复选框。在您的 getView 中,您可以检查相应的布尔值并相应地设置复选框。

      为了全部选中/取消选中,您可以修改 checkAllBoxes 中的布尔数组,然后调用 notifyDatasetChanged(),触发列表视图的更新,但仅对实际显示的项目进行操作:)

      【讨论】:

      • 第二次看到你的回答我就明白了这个问题!认为它会是这样的。感谢您的解释,也是一个很好的解决方案!
      【解决方案4】:

      mr_archano 是对的,这是因为您正在使用 convertView,它允许您重新使用/回收不再可见的视图。以下是正确执行此操作所需的:

      保存每个复选框状态的对象

      因为您的列表视图项目正在被回收,所以您不能依赖它们来保持复选框的正确状态。因此,您需要某种对象/数组来为每个列表项保存正确的复选框状态。

      从状态管理对象中读取

      然后,当您调用 GetView 时,您必须根据该对象/数组的状态将复选框的值设置为选中/未选中。

      写入状态管理对象

      您需要为每个复选框实现某种 OnChecked 侦听器,以便您可以在语句管理对象/数组中正确设置该复选框的值。

      你应该尝试使用 ViewHolder 的

      ViewHolder 非常简单。当您第一次创建视图(不使用转换视图)时,您必须调用“FindViewByID”来获取对文本框和复选框的引用。如果您将它们保存在 ViewHolder 中,您应该能够在使用 ConvertView 对象时直接引用它们,这将节省对“FindViewByID”的额外调用。看它练习要容易得多。

      本教程几乎可以满足您的需要:http://windrealm.org/tutorials/android/listview-with-checkboxes-without-listactivity.php

      【讨论】:

      • 感谢您提供更详细的解释!虽然从他刚开始就给 archano 答案
      【解决方案5】:

      我在处理大型列表时遇到了同样的问题。对于喜欢我通过谷歌在这里找到的人,我想给出我的解决方案,我认为它更容易理解。正如大家所指出的,ListView 重用了视图。所以你不应该让视图保持你的数据状态。

      我正在使用SimpleAdapter,通过设置自定义ViewBinder,我发现很容易解决这个问题。

      listItemAdapter.setViewBinder(new SimpleAdapter.ViewBinder() {
      
          public boolean setViewValue(View view, Object obj, String s) {
      
          if(view instanceof CheckBox){
              ((CheckBox)view).setChecked((Boolean)obj);
              return true;
          }
          return false;
      }
      });
      

      您需要以自己的方式保持选中/未选中状态。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-28
        • 2014-04-11
        • 1970-01-01
        • 2014-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多