Thursday, 4 November 2021

RecyclerView duplicating items after deletion from contextual action mode

In this part of my app, I am trying to implement deleting of selected favorite items via contextual action mode/bar, the problem is when I select an item then delete, it's deleted from the database and selected list but it is still available in recyclerView and it adds a duplicate from another item, the following gif clarify the problem

Here's the FavoritesPostAdapter, I deleted unrelated codes

public class FavoritesPostAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private final FragmentActivity fragmentActivity;
    private final List<FavoritesEntity> favoritesList;
    private View rootView;

    @Override
    public void setHasStableIds(boolean hasStableIds) {
        super.setHasStableIds(hasStableIds);
    }

    private static final int CARD = 0;
    private static final int CARD_MAGAZINE = 1;
    private static final int TITLE = 2;
    private static final int GRID = 3;
    private static final int SDK_VERSION = Build.VERSION.SDK_INT;
    public static final String TAG = "POST ADAPTER";

    private int viewType;
    public final Fragment fragment;
    public final PostViewModel postViewModel;
    private ActionMode mActionMode;
    private boolean multiSelection = false;
//    private int selectedPostPosition ;
    private final List<FavoritesEntity> selectedPosts = new ArrayList<>();
    private final List<RecyclerView.ViewHolder> myViewHolders = new ArrayList<>();

    public FavoritesPostAdapter(FragmentActivity fragmentActivity,
                                List<FavoritesEntity> favoritesList, Fragment fragment,
                                PostViewModel postViewModel) {
        this.fragmentActivity = fragmentActivity;
        this.favoritesList = favoritesList;
        this.fragment = fragment;
        this.postViewModel = postViewModel;
    }

    public void setViewType(int viewType) {
        this.viewType = viewType;
        notifyDataSetChanged();
    }

    public int getViewType() {
        return this.viewType;
    }

    private final ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
        @Override
        public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
            mActionMode = actionMode;
            actionMode.getMenuInflater().inflate(R.menu.favorites_contextual_menu, menu);
            applyStatusBarColor(R.color.contextualStatusBarColor);
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
            return true;
        }

        @Override
        public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
            if (menuItem.getItemId() == R.id.delete_favorites_post) {
                for (FavoritesEntity favoritesEntity : selectedPosts) {
                    postViewModel.deleteFavoritePost(favoritesEntity);
                }
                showSnackBar(selectedPosts.size() + " post/s deleted");
                multiSelection = false;
                selectedPosts.clear();
                notifyDataSetChanged();
                mActionMode.finish();
            }
            return true;
        }

        @Override
        public void onDestroyActionMode(ActionMode actionMode) {

            for (RecyclerView.ViewHolder holder : myViewHolders) {
                changePostStyle(holder, R.color.cardBackgroundColor, R.color.strokeColor);
            }

            multiSelection = false;
            selectedPosts.clear();
            applyStatusBarColor(R.color.statusBarColor);
        }
    };

    private void showSnackBar(String message){
        Snackbar.make(rootView,message,Snackbar.LENGTH_SHORT).show();
    }

    private void applyStatusBarColor(int color) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            fragmentActivity.getWindow().setStatusBarColor(ContextCompat.getColor(fragmentActivity, color));
        }
    }

    private void applySelection(RecyclerView.ViewHolder holder, FavoritesEntity currentSelectedPost) {

        if (selectedPosts.contains(currentSelectedPost)) {
            selectedPosts.remove(currentSelectedPost);
            changePostStyle(holder, R.color.cardBackgroundColor, R.color.strokeColor);
        } else {
            selectedPosts.add(currentSelectedPost);
            changePostStyle(holder, R.color.cardBackgroundLightColor, R.color.primaryColor);
        }
        applyActionModeTitle();
    }

    private void changePostStyle(RecyclerView.ViewHolder holder, int backgroundColor, int strokeColor) {
        if (holder instanceof CardViewHolder) {
            ((CardViewHolder) holder).cardLayoutBinding.mainLinearLayout.setBackgroundColor(
                    ContextCompat.getColor(fragmentActivity.getApplicationContext(),
                            backgroundColor)
            );
            ((CardViewHolder) holder).cardLayoutBinding.cardView.setStrokeColor(
                    strokeColor);
        }
    }

    private void applyActionModeTitle() {
        if (selectedPosts.size() == 0) {
            mActionMode.finish();
            multiSelection = false;
        } else if (selectedPosts.size() == 1) {
            mActionMode.setTitle(selectedPosts.size() + " item selected");
        } else {
            mActionMode.setTitle(selectedPosts.size() + " items selected");
        }
    }


    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(fragmentActivity);
        View view;

        if (this.viewType == CARD) {
            final CardLayoutBinding cardLayoutBinding
                    = CardLayoutBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
            return new FavoritesPostAdapter.CardViewHolder(cardLayoutBinding);
        } else if (this.viewType == CARD_MAGAZINE) {
            final CardMagazineBinding cardMagazineBinding
                    = CardMagazineBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
            return new FavoritesPostAdapter.CardMagazineViewHolder(cardMagazineBinding);
        } else if (this.viewType == TITLE) {
            if (SDK_VERSION < Build.VERSION_CODES.LOLLIPOP) {
                view = inflater.inflate(R.layout.title_layout_v15, parent, false);
            } else {
                view = inflater.inflate(R.layout.title_layout, parent, false);
            }
            return new FavoritesPostAdapter.TitleViewHolder(view);
        } else {
            if (SDK_VERSION < Build.VERSION_CODES.LOLLIPOP) {
                view = inflater.inflate(R.layout.grid_layout_v15, parent, false);
            } else {
                view = inflater.inflate(R.layout.grid_layout, parent, false);
            }
            return new FavoritesPostAdapter.GridViewHolder(view);
        }

    }


    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {

        myViewHolders.add(holder);
        rootView = holder.itemView.getRootView();
//        selectedPostPosition = position;

        int itemType = getViewType();
        FavoritesEntity favoriteItem = favoritesList.get(position);
        final Document document = Jsoup.parse(favoriteItem.getItem().getContent());
        final Elements elements = document.select("img");

//        Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));


        switch (itemType) {
            case CARD:
                if (holder instanceof FavoritesPostAdapter.CardViewHolder) {
                    ((FavoritesPostAdapter.CardViewHolder) holder).bind(favoriteItem);

                    ((CardViewHolder) holder).cardLayoutBinding.cardView.setOnClickListener(view -> {
                                if (multiSelection) {
                                    applySelection(holder, favoriteItem);
                                } else {
                                    mActionMode.finish();
                                    if (Objects.requireNonNull(Navigation.findNavController(
                                            view
                                    ).getCurrentDestination()).getId() == R.id.nav_favorites) {
                                        Navigation.findNavController(view)
                                                .navigate(FavoritesFragmentDirections
                                                        .actionFavoritesFragmentToDetailsFragment(favoriteItem.getItem()));
                                    }
                                }
                            }
                    );
                    ((CardViewHolder) holder).cardLayoutBinding.cardView.setOnLongClickListener(view -> {
                        if (!multiSelection) {
                            multiSelection = true;
                            fragmentActivity.startActionMode(mActionModeCallback);
                            applySelection(holder, favoriteItem);
                            return true;
                        } else {
                            applySelection(holder, favoriteItem);
                            return true;
                        }

                    });
                }

                break;

favorites_contextual_menu xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/delete_favorites_post"
        android:title="@string/delete_post"
        app:showAsAction="ifRoom"
        app:iconTint="@color/white"
        android:icon="@drawable/ic_delete"
        >

    </item>

</menu>

PS: I tried to get the selected position from the holder and assign it to selectedPostPosition int value to use it in notifyItemRemoved(position); and notifyItemRangeChanged(position, getItemCount()); like in this answer but it doesn't fix the issue



from RecyclerView duplicating items after deletion from contextual action mode

No comments:

Post a Comment