【问题标题】:Check and Uncheck All CheckBoxes in ListView选中和取消选中 ListView 中的所有复选框
【发布时间】:2014-09-21 11:26:18
【问题描述】:

我很惊讶我在 Stack 上找不到可用的现有答案,所以我在这里。

我有一个 ListFragment,其中包含一个附加到 SimpleCursorAdapter 的列表,该列表由以下 row.xml 文件定义的行组成:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:orientation="vertical"
   android:padding="6dip" >

   <CheckBox
       android:id="@+id/story_check_box"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:focusable="false"
       android:focusableInTouchMode="false" />

   <TextView
       android:id="@+id/story"
       android:layout_width="wrap_content"
       android:layout_height="24sp"
       android:lines="1"
       android:scrollHorizontally="true"
       android:singleLine="true"
       android:layout_alignBaseline="@+id/story_check_box"
       android:layout_alignBottom="@+id/story_check_box"
       android:layout_toRightOf="@+id/story_check_box" />

</RelativeLayout>

我在我的 ListFragment 中使用以下代码将列表与适配器连接起来:

adapter = new SimpleCursorAdapter(getActivity(), R.layout.row, null, new String[] { CProvider.Stories.TITLE }, new int[] { R.id.story }, 0);
setListAdapter(adapter);

然后我尝试在我的片段中使用 CheckBox 来切换所有列表复选框,如下所示:

CheckBox selectAll = (CheckBox) rootView.findViewById(R.id.select_check_box);
    selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            final ListView listView = getListView();
            for(int i=0; i < getListAdapter().getCount(); i++){
                View view = getViewByPosition(i, listView);
                CheckBox cb = (CheckBox)view.findViewById(R.id.story_check_box);
                if (isChecked) {
                    cb.setChecked(true);
                }
                else {
                    cb.setChecked(false);
                }
            }
        }

    });

我从这里得到getViewByPositionGet ListView children that are not in view几乎可以工作,但是一些复选框没有被选中(并且有一个模式,但我可以似乎没有弄清楚)。它似乎也比我认为必要的更笨拙。

我想要左边的复选框,所以我不想使用checkedtextviews。也许我需要扩展 CursorAdapter 并覆盖 getView?

提前致谢。

【问题讨论】:

  • 关于左侧的复选框,请考虑composite view。您也可以让该视图实现Checkable,这样当它被选中时(通过listView.setItemChecked),您将内部复选框设置为选中。

标签: android android-listview


【解决方案1】:

也许我没有正确理解您的问题,但我的理解是您想要选中和取消选中所有复选框,这要归功于一个“全选复选框”。

然后,我要做的是将“选择所有复选框”的状态作为类的变量(作为布尔值),该变量被您的 selectAll.setOnCheckedChangeListener 覆盖,并对适配器说“嘿,我的状态变了!”每次复选框更改其状态时。 像这样的:

class Dummy{
    boolean isAllSelected = false;
    Checkbox selectAll = (find or create your CheckBox)
    selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (isChecked) isAllSelected = true;
            else isAllSelected = false;
            listView.getAdapter().notifyDataSetChanged();
        }
    }

然后,您只需覆盖此适配器的 getView()(如您建议的那样)添加“if (isAllSlected)”条件。

对我来说,这听起来最简单,但每次用户单击复选框时调用 notifyDataSetChanged() 方法可能不太好(对于如此小的更改来说效率不高)。无论如何,希望对您有所帮助(我写的代码可能语法不正确:我是直接在网站表单上写的)!

【讨论】:

  • 这看起来很有希望。感谢您的回答。为了方便起见,我现在只是使用“内置”列表视图选择机制,所以我可以继续前进,但我应该有机会尽快尝试实施和测试你的答案。
【解决方案2】:

以下是我最终要做的事情。除了处理“全选/取消全选”功能外,它还处理选中/取消选中列表项文本时选中/取消选中复选框,反之亦然。我担心getView 被频繁调用,但是setItemChecked 无论如何都会导致getView 被调用,因此可以避免多少调用getView 是有限度的。正如 ataulm 在评论中提到的那样,也许复合视图会是一种不太麻烦的解决方案。

onCreateView:

selectAllCheckBox = (CheckBox) rootView.findViewById(R.id.select_all_check_box);
    selectAllCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            final ListView listView = getListView();
            for(int i=0; i < getListAdapter().getCount(); i++){
                listView.setItemChecked(i, isChecked);
            }
        }
    });

我还使用以下代码创建了一个自定义 SimpleCursorAdapter,它也使用了一个简单的 ViewHolder 类。在getView 中,我检查列表中的哪些项目被选中,并检查与这些项目相对应的复选框。如果相应的复选框已被单击(即选中或未选中),还有一些代码可以将列表项设置为选中或不选中。

class AvailableCursorAdapter extends SimpleCursorAdapter {
    AvailableCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
        super(context, layout, c, from, to, flags);
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        View row = super.getView(position, convertView, parent);
        ViewHolder holder = (ViewHolder)row.getTag();       
        if (holder == null) {                         
            holder = new ViewHolder(row);
            row.setTag(holder);
        }
        holder.storyCheckBox.setChecked(false);
        holder.story.setTextColor(Color.LTGRAY);
        long [] checkedIds = getListView().getCheckedItemIds();
        if (checkedIds != null) {
            for (int i = 0; i < checkedIds.length; i++) {
                if (checkedIds[i] == getListAdapter().getItemId(position)) {
                    holder.storyCheckBox.setChecked(true);
                    holder.story.setTextColor(Color.WHITE);
                    break;
                }
            }
        }
        final boolean isChecked = holder.storyCheckBox.isChecked();
        holder.storyCheckBox.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                getListView().setItemChecked(position, !isChecked);
            }
        });
        return(row);
    }
}

class ViewHolder {
    CheckBox storyCheckBox;
    TextView story = null;

    ViewHolder(final View row) {
        storyCheckBox = (CheckBox) row.findViewById(R.id.story_check_box);
        story = (TextView) row.findViewById(R.id.story);
    }
}

最后,以下代码会在单击单个 ListItem 时调用 getView,以便相应地选中或取消选中其对应的复选框:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    ViewHolder holder = (ViewHolder) v.getTag();
    holder.storyCheckBox.setChecked(false);
    holder.story.setTextColor(Color.LTGRAY);
    long [] checkedIds = l.getCheckedItemIds();
    if (checkedIds != null) {
        for (int i = 0; i < checkedIds.length; i++) {
            if (checkedIds[i] == getListAdapter().getItemId(position)) {
                holder.storyCheckBox.setChecked(true);
                holder.story.setTextColor(Color.WHITE);
                break;
            }
        }
    }
}

【讨论】:

  • 我还没有阅读完整的答案,但根据最后一段,有一些奇怪的地方:当你选择/取消选择一个项目时,你在适配器上调用notifyDatasetChanged(),表明数据已更新。对我来说,这是一种代码味道 - selection 不应该修改底层数据集,选择应该是视图的属性。 对所选项目执行的操作是应该修改数据集的操作。
  • @ataulm 我同意这里有些“臭”,我在解决方案顶部的新编辑中提到了这一点。我正在调用notifyDatasetChanged(),以便调用getView,因为那是我处理复选框和文本视图的地方,但如果可以在该代码之外轻松找到它们,那么这样做似乎是有意义的.
  • @ataulm 复合视图看起来很有希望,尽管我可能会保留上面的代码,因为我认为它不会产生真正的性能问题。我只是不一定会推荐它。我认为我的解决方案不应该是 +1。如果/当我想出更好的东西时,我会更新它。
  • onListItemClick 中删除了notifyDatasetChanged
  • 数组是否会为空(文档没有说可以),您的适配器是否有稳定的 ID? -> 如果没有(Adapter.hasStableIds() 返回false)那么这可能并不总是有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 2020-12-12
相关资源
最近更新 更多