Android 删除ArrayList上的元素会在ArrayAdapter上引发IndexOutOfBoundsException

Android 删除ArrayList上的元素会在ArrayAdapter上引发IndexOutOfBoundsException,android,android-arrayadapter,indexoutofboundsexception,Android,Android Arrayadapter,Indexoutofboundsexception,我正在开发一个Android 3.1应用程序 我有我的定制阵列适配器。我想在列表视图中显示姓名列表 这些名称是可以下载并在本地保存的表单。当用户下载并保存一个或多个时,我调用updateFormsNotDownloaded()。但当我这么做的时候,我会有一种非同寻常的感觉。我认为这个问题是因为我调用了notifyDataSetChanged() 看看我的代码: public class FormAdapter extends ArrayAdapter<Form> { priv

我正在开发一个Android 3.1应用程序

我有我的定制阵列适配器。我想在列表视图中显示姓名列表

这些名称是可以下载并在本地保存的表单。当用户下载并保存一个或多个时,我调用
updateFormsNotDownloaded()
。但当我这么做的时候,我会有一种非同寻常的感觉。我认为这个问题是因为我调用了
notifyDataSetChanged()

看看我的代码:

public class FormAdapter extends ArrayAdapter<Form>
{
    private Context context;
    private int layoutResourceId;
    private List<Form> forms;
    private ArrayList<Integer> checkedItemsPosition;
    private Button downloadButton;

    public ArrayList<Integer> getCheckedItemsPosition()
    {
        return checkedItemsPosition;
    }

    public String[] getSelectedFormsId()
    {
        String[] ids = new String[checkedItemsPosition.size()];
        int i = 0;
        for(Integer pos : checkedItemsPosition)
        {
            Form f = forms.get(pos.intValue());
            ids[i] = f.FormId;
            i++;
        }
        return ids;
    }

    /**
     * Called when selected forms has been downloaded and save it locally correctly.
     */
    public void updateFormsNotDownloaded()
    {
        ArrayList<Form> copyForms = new ArrayList<Form>();
        for (int i = 0; i < forms.size(); i++)
        {
            if (!checkedItemsPosition.contains(new Integer(i)))
                copyForms.add(forms.get(i));
        }
        forms = copyForms;
        checkedItemsPosition.clear();
        notifyDataSetChanged();
    }

    public FormAdapter(Context context, int textViewResourceId,
            List<Form> objects, Button downloadButton)
    {
        super(context, textViewResourceId, objects);

        this.context = context;
        this.layoutResourceId = textViewResourceId;
        this.forms = objects;
        this.checkedItemsPosition = new ArrayList<Integer>();
        this.downloadButton = downloadButton;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent)
    {
        View row = convertView;
        if (row == null)
        {
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, parent, false);
        }

        Form f = forms.get(position);
        if (f != null)
        {
            CheckBox checkBox = (CheckBox)row.findViewById(R.id.itemCheckBox);
            if (checkBox != null)
            {
                checkBox.setText(f.Name);
                checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener()
                {
                    public void onCheckedChanged(CompoundButton buttonView,
                            boolean isChecked)
                    {
                        //Form f = forms.get(position);
                        if (isChecked)
                        {
                            //checkedItems.add(f.FormId);
                            checkedItemsPosition.add(new Integer(position));
                        }
                        else
                        {
                            //checkedItems.remove(checkedItems.indexOf(f.FormId));
                            checkedItemsPosition.remove(checkedItemsPosition.indexOf(new Integer(position)));
                        }
                        downloadButton.setEnabled(checkedItemsPosition.size() > 0);
                    }
                });
            }
        }

        return row;
    }
}

位置是基于1的,而数组索引是基于零的

因此,改变这一行:

if (!checkedItemsPosition.contains(new Integer(i)))
    copyForms.add(forms.get(i - 1));

重写getCount()。然后返回其中的值forms.size()。

您使用的是两个
列表及其索引,但这些列表无法同步(它们可以单独更改,而不必检查其他列表)

为什么不改为使用
ArrayList checkForms
ArrayList uncheckedForms
,然后您可以从
uncheckedForms
中删除对表单的引用,并将其添加到
checkedForms
中,这将保持这两个
列表的同步


当您需要获取所有表单时,您可以简单地返回两个
ArrayList
s的并集。

在我的例子中,Shubhayu的答案是不够的。getCount和getItem必须同步并使用相同的列表对象。如果您的阵列适配器必须使用项目的内部列表,则两者都需要重写

public class MyArrayAdapter extends ArrayAdapter<MyItemType> {

private final List<MyItemType> items;

public MyArrayAdapter(final Context _context, final int _resource, final List<MyItemType> _items) {
  super(_context, _resource, _items);

  this.items = _items;
}

// IMPORTANT: either override both getCount and getItem or none as they have to access the same list
@Override
public int getCount() {
  return this.items.size();
};

@Override
public Site getItem(final int position) {
  return this.items.get(position);
}

...
公共类MyArrayAdapter扩展了ArrayAdapter{
私人最终清单项目;
公共MyArrayAdapter(最终上下文、最终整数资源、最终列表项){
超级(\u上下文、\u资源、\u项目);
this.items=\u items;
}
//重要提示:要么重写getCount和getItem,要么重写none,因为它们必须访问相同的列表
@凌驾
public int getCount(){
返回此.items.size();
};
@凌驾
公共站点getItem(最终整型位置){
返回此.items.get(位置);
}
...

还没有人回答他为什么会出现异常。因此,尽管他的问题已经很老了,我还是会在这里给出我的答案

出现异常的原因是,当您更新“表单”时,您创建了一个新的数组对象(因此是一个新的引用),并将表单的引用更改为它,而ArrayAdapter维护自己的数组对象mobject,构造函数将您提供的数组对象(对象)的引用复制到该对象。如果您查看它的源代码,就可以看到这一点(幸好它是开源的)


要真正解决这个问题,您应该使用继承的函数add(…)、addAll(…)等更新数组,或者只是扩展baseadapter并创建您自己的实现。

表单初始化在哪里?表单是在异步任务上初始化的。我在这里没有看到它,但我假设您已经重写了getCount()“?@Shubhayu不,我没有这样做。我正在阅读这个问题。我认为这是一个比我更好的方法。当你从它扩展到最适合你的时候,你可以改变它的行为。但是在这种情况下,我认为覆盖getCount()应该可以解决ure问题。不过,您可以始终优化ure代码以使其更加健壮:)当我选择第一个ListView项时,checkedItemsPosition包含0。@WarrenFaith我似乎不知道这在这里是如何应用的。在本例中的“I”来自0-forms.size()-1.我认为我们不需要这里的1/0检查。我是否遗漏了什么?这应该是公认的答案。仅覆盖
getCount
无效。
public class MyArrayAdapter extends ArrayAdapter<MyItemType> {

private final List<MyItemType> items;

public MyArrayAdapter(final Context _context, final int _resource, final List<MyItemType> _items) {
  super(_context, _resource, _items);

  this.items = _items;
}

// IMPORTANT: either override both getCount and getItem or none as they have to access the same list
@Override
public int getCount() {
  return this.items.size();
};

@Override
public Site getItem(final int position) {
  return this.items.get(position);
}

...