Android 适配器 "java.lang.IndexOutOfBoundsException: Invalid index 4, size is 4"

Android Adapter "java.lang.IndexOutOfBoundsException: Invalid index 4, size is 4"

我在从 ArrayList 中删除项目和同步 Adapter 时遇到问题。 我有我的 RecyclerView 适配器,里面有一些 ArrayList,叫做 items。我从服务器下载了一些列表并在其中显示。每当我单击某些列表项时,我想将其从服务器、本地 ArrayList 中删除并通知适配器。问题是,当我从列表中删除从 downup 的所有内容时,一切正常,但是当 f.e 时。我从列表中删除第一个元素,然后随机删除它在我单击的元素之后删除的一些元素。在某些情况下,应用程序会崩溃(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);
    }
}

使用

notifyItemRangeChanged(position, getItemCount()); 

之后
notifyItemRemoved(position);

不需要使用索引,只需要使用位置。

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

建议:

  1. 在add/remove操作的情况下,对传递给适配器的主数组(例如ArrayList)执行
  2. 永远不要将主数组(传递给适配器)初始化为 null,它会丢失适配器引用
  3. 在 adding/removing 之后,只需为适配器调用 adapter.notifyDataSetChanged();

解决方案 - 希望这会对某些人有所帮助

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

如果列表中有 4 个显示元素 f.e。 [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)。但我们也有 returned holder。如果我们使用名为的方法:getAdapterPosition() from class RecyclerView.ViewHolder 我们就在家了。

getAdapterPosition

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

这将始终 return 与 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
            }

        });
    }

只需使用

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