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

标签 android indexing android-recyclerview notifydatasetchanged

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

java.lang.IndexOutOfBoundsException: 索引 4 无效,大小为 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);
    }
}

最佳答案

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

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

如果您在一个列表中显示了 4 个元素,例如[0, 1, 2, 3] 并尝试从列表的末尾删除一切都会好起来的,因为 clicked positions 将与中的 positions 完全匹配数组列表。例如,如果您单击第 4 个元素:

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

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

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

在这种情况下,我要获取的 ArrayList 将如下所示:[0, 1, 3]。在我现在点击最后一个显示的元素并想删除它,这就是我将得到的:

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

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

一开始我们有:

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

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

删除第三个元素后:

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

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

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

onBindViewHolder(ViewHolder holder, int position) 中我们使用了 position 从收藏夹中删除(位置)。但我们也有返回的 holder。如果我们使用名为 getAdapterPosition() 的方法,来自 RecyclerView.ViewHolder 类,我们就在家了。

获取适配器位置

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

这将始终返回与 ArrayList 中相同的索引。所以总结一下,我们所要做的就是用 holder.getAdapterPosition() 改变 position 参数:

// 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
            }

        });
    }

关于安卓适配器 "java.lang.IndexOutOfBoundsException: Invalid index 4, size is 4",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34457836/

相关文章:

android - 在底部对齐 RecyclerView 中的项目

android - 仅某些设备中的Android/NDK SIGILL错误

android - 无法在 AsyncTask 中访问 "findViewById"

python - Pandas 在 bool 索引匹配周围获取 N 行

indexing - Sitecore 9.1 Azure 主索引重建卡住

sql - 如何在SQL Server中为group by构造索引

android - 允许 Recyclerview 项目滚动到 Recyclerview 顶部

java - RecyclerView崩溃

java - 如何更改 ContextMenu 菜单项的文本?

android - isAttachedToWindow() 返回 false