Android Adapter" java.lang.IndexOutOfBoundsException:索引4无效,大小为4"

时间:2015-12-24 20:57:50

标签: android indexing android-recyclerview notifydatasetchanged

我从ArrayList删除项目并同步Adapter时遇到问题。 我的RecyclerView适配器里面有一些名为ArrayList items的{​​{1}}。我从服务器下载了一些列表,并在其中显示。每当我点击一些列表项时,我想从服务器,本地ArrayList中删除它,并通知适配器。问题是,当我从列表中删除downup的所有内容时,一切正常,但是当f.e.我从列表中删除了第一个元素,然后随机删除了我点击后删除元素的一些元素。在某些情况下,应用程序崩溃(例如,我删除第一个元素,然后删除最后一个元素)。我得到的错误是f.e。:

java.lang.IndexOutOfBoundsException: Invalid index 4, size is 4

看起来像是列表大小的东西,但我不知道出了什么问题?

这是我从(setPopUpListener(popupMenu, position))得到位置的函数:

// Binding New View
    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        RecipeItem item = items.get(position);

        // Binding Recipe Image
        Picasso.with(context).load(item.getImgThumbnailLink()).into(holder.recipeItemImage);
        // Binding Recipe Title
        holder.recipeItemTitle.setText(item.getTitle());
        // Binding Recipe Subtitle
        String subtitle = "Kuchnia " + item.getKitchenType() + ", " + item.getMealType();
        holder.recipeItemSubtitle.setText(subtitle);
        // Binding Recipe Likes Count
        holder.recipeItemLikesCount.setText(Integer.toString(item.getLikeCount()));
        // Binding Recipe Add Date
        holder.recipeItemAddDate.setText(item.getAddDate());
        // Binding Recipe Options Icon
        holder.recipeItemOptionsIcon.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                PopupMenu popupMenu = new PopupMenu(context, v);
                setPopUpListener(popupMenu, position);   // Setting Popup Listener
                inflatePopupMenu(popupMenu);             // Inflating Correct Menu
                popupMenu.show();
            }

        });
        // Item Click Listener
        holder.setClickListener(new RecipeItemClickListener() {

            @Override
            public void onClick(View view, int position) {
                // taking to recipe activity
            }

        });
    }

以下是setPopUpListener() - 只需查看removeFromFavourites(position)

// Setting Popup Listener
private void setPopUpListener(PopupMenu popupMenu, final int position) {
    popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {

        @Override
        public boolean onMenuItemClick(MenuItem item) {
            switch (popupType) {
                // Add To Favourites Menu
                case 0: {
                    switch (item.getItemId()) {
                        case R.id.item_add: {
                            addToFavourites(position);
                            return true;
                        }
                    }
                }

                // Remove From Favourites Menu
                case 1: {
                    switch (item.getItemId()) {
                        case R.id.item_remove: {
                            removeFromFavourites(position);
                            return true;
                        }
                    }
                }
            }
            return false;
        }

    });
}

此处出现错误(removeFromFavourites(position)):

  // Removing User's Favourite
private void removeFromFavourites(int position) {
    // Checking Connection Status
    if (!FormValidation.isOnline(context)) {
        showSnackbarInfo(context.getString(R.string.err_msg_connection_problem),
                R.color.snackbar_error_msg);
    } else {
        SQLiteHandler db = new SQLiteHandler(context);
        // Getting User Unique ID
        String userUniqueId = db.getUserUniqueId();
        db.close();

        RecipeItem listItem = items.get(position);
        // Getting Recipe Unique ID
        String recipeUniqueId = listItem.getUniqueId();

        // Removing From User's Favourites
        removeFromUserFavouritesOnServer(recipeUniqueId, userUniqueId);

        // Removing Item From Local Array List
        items.remove(position);

        // Notifying Adapter That Item Has Been Removed
        notifyItemRemoved(position);
    }
}

4 个答案:

答案 0 :(得分:17)

解决方案 - 希望这将有助于某些人

我已经为此找到了解决方案。如果有人试图从他的数组列表中动态删除元素,notifyItemRemoved(position)不会将点击的位置作为onBindViewHolder(ViewHolder holder, int position)内的参数发送。你将遇到与我完全相同的情况。

如果列表f.e中有4个显示的元素。 [0, 1, 2, 3]并尝试从列表末尾删除一切正常,因为clicked positions将完全匹配positions in ArrayList。例如,如果单击第4个元素:

position = 3 - 单击列表元素时将获得的位置; myArray.remove(position) - 将删除带有index = 3notifyItemRemoved(position)的元素 - 将为列表设置动画并从显示的列表中删除已删除的元素。您将拥有以下列表:[0, 1, 2]。这很好。

当您要删除随机元素时,情境会发生变化。让我们说我想要删除第三个显示的列表元素。我点击它删除所以我得到:

position = 2 - > myArray.remove(position) - > notifyItemRemoved(position)

在这种情况下,我将获得的ArrayList将是这样的:[0, 1, 3]。在我现在点击最后一个显示的元素,并希望删除它将得到的内容:

position = 3 - > myArray.remove(position) - > notifyItemRemoved(position)

但是会发生什么?应用突然崩溃,例外:java.lang.IndexOutOfBoundsException: Invalid index 3, size is 3。这意味着我们试图将元素放在不存在的位置。但为什么?我从元素中获得了我点击的位置......这就是发生的事情:

At the beggining we had:

阵列列表索引 - > [0, 1, 2, 3]

点击位置 - > [0, 1, 2, 3]

After Deleting 3rd element:

阵列列表索引 - > [0, 1, 2]

点击位置 - > [0, 1, 3]

现在,当我尝试删除position = 3处的元素时,我们无法做到这一点。我们没有那个职位。我们可以得到的最大位置是2。这就是为什么我们得到例外。如何解决这个问题?

onBindViewHolder(ViewHolder holder, int position)中,我们使用了position removeFromFavourites(position)。但我们也退回了holder。如果我们使用来自班级getAdapterPosition()的名为RecyclerView.ViewHolder的方法,我们就在家。

getAdapterPosition

来自开发者网站:http://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#getAdapterPosition()

这将始终返回与ArrayList中的索引相同的索引。因此,总结我们所要做的就是将position参数更改为holder.getAdapterPosition()

// Binding New View
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        RecipeItem item = items.get(position);

        // Binding Recipe Image
        Picasso.with(context).load(item.getImgThumbnailLink()).into(holder.recipeItemImage);
        // Binding Recipe Title
        holder.recipeItemTitle.setText(item.getTitle());
        // Binding Recipe Subtitle
        String subtitle = "Kuchnia " + item.getKitchenType() + ", " + item.getMealType();
        holder.recipeItemSubtitle.setText(subtitle);
        // Binding Recipe Likes Count
        holder.recipeItemLikesCount.setText(Integer.toString(item.getLikeCount()));
        // Binding Recipe Add Date
        holder.recipeItemAddDate.setText(item.getAddDate());
        // Binding Recipe Options Icon
        holder.recipeItemOptionsIcon.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                PopupMenu popupMenu = new PopupMenu(context, v);
                setPopUpListener(popupMenu, holder.getAdapterPosition());   // Setting Popup Listener
                inflatePopupMenu(popupMenu);             // Inflating Correct Menu
                popupMenu.show();
            }

        });
        // Item Click Listener
        holder.setClickListener(new RecipeItemClickListener() {

            @Override
            public void onClick(View view, int position) {
                // taking to recipe activity
            }

        });
    }

答案 1 :(得分:3)

使用notifyItemRangeChanged(position,getItemCount());在notifyItemRemoved(position)之后; 您不需要使用索引,只需使用position。

答案 2 :(得分:0)

您正在更改内部数据结构,这不是一个好方法

建议:

  1. 如果是添加/删除操作,请执行传递给适配器的主数组(例如ArrayList)
  2. 永远不要将主数组(传递给适配器)初始化为null,否则会丢失适配器引用
  3. 添加/删除只需为适配器调用adapter.notifyDataSetChanged();

答案 3 :(得分:0)

直接使用

        product_list.remove(product_list.get(position));
        notifyItemRemoved(position);
        notifyItemRangeChanged(position, itemCount);