listView 中复选框的奇怪行为
Posted
技术标签:
【中文标题】listView 中复选框的奇怪行为【英文标题】:Strange behaviour of checkBox in the listView 【发布时间】: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_
android:layout_
android:layout_toEndOf="@id/rl1_List"
android:layout_centerInParent="true">
<CheckBox
android:id="@+id/cbList_hook"
android:layout_
android:layout_
android:focusable="false"
android:focusableInTouchMode="false"
android:checked="false"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl3_List"
android:layout_
android:layout_
android:layout_toEndOf="@id/rl2_List"
android:layout_alignParentEnd="true"
android:layout_marginStart="100dp">
<ImageView
android:id="@+id/ivList_delete"
android:layout_
android:layout_
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的回收机制,看这个解释:***.com/a/14108676/4224337。在 getView() 方法中,您必须重新检查或重新取消检查 CheckBoxes 取决于它们的状态(选中/未选中) 使用 cb.setTag(position) 试试这个***.com/questions/28275273/… 【参考方案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。 如果它不能解决问题,那么它在技术上也不是一个答案,是吗? 您能否编辑您的答案以提供一些解释。不仅仅是一段代码。以上是关于listView 中复选框的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章
QT 5.1.1:QWebview 中的复选框在 Win 7 (x64) / Win 8 下显示奇怪的行为