【问题标题】:Strange behaviour of checkBox in the listViewlistView 中复选框的奇怪行为
【发布时间】:2015-04-01 08:48:43
【问题描述】:

我有一个带有listView的片段,listview布局包含(TextView、checkBox和ImageView如下

t1........CB.......IV
t2........CB.......IV
t3........CB.......IV
t4........CB.......IV
t5........CB.......IV
t6........CB.......IV
      SaveButton

在 getView() 方法中,我检查复选框是否被选中,如果被选中,我将选中的项目添加到列表中。

问题是,最初我将所有复选框设置为未选中,但是当我选中第一项时,令人惊讶的是,自动选中下面的第三项,如果我从上面选中第二项,则第二项从下面检查,如果我从上面检查第三项,那么最后一项会自动检查?!!

请看一下 getView() 方法,让我知道我错过了什么:

getView()

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub

    if (convertView == null) {
        LayoutInflater layoutinflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        convertView = layoutinflater.inflate(R.layout.list_items_layout, null);
    }

    if (this.checkedItemsList == null) {
        this.checkedItemsList = new ArrayList<String>();
    }

    final TextView tv = (TextView) convertView.findViewById(R.id.tvlist_topic);
    final CheckBox cb = (CheckBox) convertView.findViewById(R.id.cbList_hook);
    final ImageView iv = (ImageView) convertView.findViewById(R.id.ivList_delete);

    tv.setText(this.topicsList.get(position));

    cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {

        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            // TODO Auto-generated method stub
            if (isChecked) {
                checkedItemsList.add(topicsList.get(position));
                setCheckedItemsList(checkedItemsList);
                Log.d(TAG, "size: " + checkedItemsList.size());
            }
        }
    });

    iv.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            if (cb.isChecked())
                cb.setChecked(false);

            topicsList.remove(position);
            notifyDataSetChanged();
        }
    });

    return convertView;
}

xml

<RelativeLayout 
    android:id="@+id/rl2_List"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toEndOf="@id/rl1_List"
    android:layout_centerInParent="true">
    <CheckBox
        android:id="@+id/cbList_hook"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:checked="false"/>
</RelativeLayout>

<RelativeLayout 
    android:id="@+id/rl3_List"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toEndOf="@id/rl2_List"
    android:layout_alignParentEnd="true"
    android:layout_marginStart="100dp">
    <ImageView 
        android:id="@+id/ivList_delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:clickable="true"
        android:src="@drawable/delete_icon"
        android:contentDescription="icon to delete item from the Listview"/>
</RelativeLayout>

【问题讨论】:

  • 你可以为这堆 tb -- cb -- iv 发布你的 xml 吗?
  • 好的,但在 xml 中,我最初将复选框设置为 false
  • 由于ListView的回收机制,看这个解释:stackoverflow.com/a/14108676/4224337。在 getView() 方法中,您必须重新检查或重新取消检查 CheckBoxes 取决于它们的状态(选中/未选中)
  • 使用 cb.setTag(position)

标签: android listview checkbox android-listview


【解决方案1】:

你必须保持你的对象检查状态。

我会在您的对象中(在主题列表中)实现一个实例变量,以指示它是否被选中。

类似

public class Topic{

public boolean isChecked;

... //Other instance variables

public boolean isChecked() {
    return isChecked;
}

public void setChecked(boolean isChecked) {
    this.isChecked = isChecked;
}

...
//other getters and setters

}

然后在您的 listAdapter getView 方法中,检查此值并相应地设置复选框。

final CheckBox cb = (CheckBox) convertView.findViewById(R.id.cbList_hook);

boolean isChecked = topicsList.get(position).isChecked();

cb.setChecked(isChecked);

//Set a tag on the cb, to know which object it corresponds with
cb.setTag(topicsList.get(position));

此外,您必须在单击对象时更改对象检查值。

cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        // TODO Auto-generated method stub
        if (isChecked) {
            checkedItemsList.add(topicsList.get(position));
            setCheckedItemsList(checkedItemsList);
            Log.d(TAG, "size: " + checkedItemsList.size());
        }
        CheckBox cb = (CheckBox) buttonView;
        Object yourObject = (Object) buttonView.getTag();
        yourObject.setChecked(isChecked);
    }
});

【讨论】:

    【解决方案2】:

    你需要了解convertView,对于初学者来说往往比较困惑。

    例如,如果您在一个巨大的列表中有 100 个 Checkbox,但一次只有 5 个在屏幕上可见,那么幼稚的方法是为 Checkbox 创建 100 个对象(事实上,这就是它在早期版本的 Android 中的工作方式) .但是,创建新对象不仅在创建期间会消耗 CPU,而且在垃圾收集期间也会消耗 CPU。这就是使用 convertView 的原因。 Android 系统只会创建 5 个对象,并在用户滚动时重复使用相同的对象。就您而言,我认为屏幕上只有 3 个对象。因此,从技术上讲,第一个滚出屏幕的对象和第四个出现在屏幕上的对象是同一个对象。

    您的解决方案是将View 对象视为可视元素并将状态保存在其他位置。我认为另一个答案建议使用代码,因此,我将避免建议代码并鼓励您理解这个概念并自己找出解决方案。

    【讨论】:

    • 我现在很困惑。
    【解决方案3】:

    如果你使用ViewHolder 模式会更好。它将防止每次用户滚动列表时增加视图的开销。此外,数据源中是否应该存在复选框(您用于填充 ListView 的数据结构;在本例中为主题列表)。假设数据结构的模型类命名为Model:

    public class Model {
     private boolean mChecked = false;
    
     public boolean isChecked() {
      return this.mChecked;
     }
     public void setChecked(boolean checked) {
      this.mChecked = checked;
     }
    }
    

    现在在 ListView 的适配器类中

    public class SomeAdapter extends BaseAdapter {
    
     private ArrayList<Model> topicsList; //assuming this is filled with data
     static class ViewHolder { //the view holder's class
      TextView text;
      CheckBox checkBox;
      ImageView imageView;
     }
    
    
     @Override
     public View getView(final int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        ViewHolder holder;
        if (convertView == null) {
            LayoutInflater layoutinflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
            convertView = layoutinflater.inflate(R.layout.list_items_layout, null);
            TextView tv = (TextView) convertView.findViewById(R.id.tvlist_topic);
            CheckBox cb = (CheckBox) convertView.findViewById(R.id.cbList_hook);
            ImageView iv = (ImageView) convertView.findViewById(R.id.ivList_delete);
            holder = new ViewHolder(); 
            holder.textView = tv;
            holder.checkBox = cb;
            holder.imageView = iv;
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) converView.getTag();
        }
    
        if (this.checkedItemsList == null) {
            this.checkedItemsList = new ArrayList<String>();
        }
    
    
        TextView textView = viewHolder.textView;
        CheckBox checkBox = viewHolder.checkBox;
        ImageView imageView = viewHolder.imageView;    
    
        textView.setText(this.topicsList.get(position));
        if (this.topicsList.get(position).isChecked()) {
         checkBox.setChecked(true);
        } else {
         checkBox.setChecked(false);
        }
    
        checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // TODO Auto-generated method stub
                if (isChecked) {
                    SomeAdapter.this.topicsList.get(position).setChecked(isChecked);
                  checkedItemsList.add(SomeAdapter.this.topicsList.get(position));
                    setCheckedItemsList(checkedItemsList);
                    Log.d(TAG, "size: " + checkedItemsList.size());
                }
            }
        });
    
        imageView.setOnClickListener(new OnClickListener() {
    
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                if (checkBox.isChecked()) {
                    checkBox.setChecked(false);
                    SomeAdapter.this.topicsList.get(position).setChecked(false);
                    topicsList.remove(position);
                    notifyDataSetChanged();
                } else { //if needed
                    checkBox.setChecked(true); 
                    SomeAdapter.this.topicsList.get(position).setChecked(true);
                    //perform other operations here
                }
    
            }
        });
    
        return convertView;
     }
    }
    

    【讨论】:

      【解决方案4】:

      你必须这样做

      @Override
      public View getView(final int position, View convertView, ViewGroup parent) {
      // TODO Auto-generated method stub
      
      if (convertView == null) {
          LayoutInflater layoutinflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
          convertView = layoutinflater.inflate(R.layout.list_items_layout, null);
      }
      
      if (this.checkedItemsList == null) {
          this.checkedItemsList = new ArrayList<String>();
      }
      
      final TextView tv = (TextView) convertView.findViewById(R.id.tvlist_topic);
      final CheckBox cb = (CheckBox) convertView.findViewById(R.id.cbList_hook);
      final ImageView iv = (ImageView) convertView.findViewById(R.id.ivList_delete);
      
      tv.setText(this.topicsList.get(position));
      cb.setTag(position);
      cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
      
          @Override
          public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
              // TODO Auto-generated method stub
              if (isChecked) {
                  checkedItemsList.add(topicsList.get(Integer.parseInt(buttonView.getTag().toString())));
                  setCheckedItemsList(checkedItemsList);
                  Log.d(TAG, "size: " + checkedItemsList.size());
              }
          }
      });
      
      
      
      return convertView;
      }
      

      【讨论】:

      • 这并不能完全解决问题,但他可以绕过它来解决他的问题。这将有助于准确设置checkedItemsList。
      • 如果它不能解决问题,那么它在技术上也不是一个答案,是吗?
      • 您能否编辑您的答案以提供一些解释。不仅仅是一段代码。
      猜你喜欢
      • 2023-03-20
      • 1970-01-01
      • 2020-08-29
      • 2016-11-27
      • 1970-01-01
      • 2010-10-08
      • 2011-08-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多