package com.badoo.chateau.extras; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import java.util.HashSet; import java.util.Set; /** * Helper for managing the selection state when performing multiple selection in a RecyclerView. */ public class MultiSelectionHelper { @IntDef({MODE_SINGLE_SELECT, MODE_MULTIPLE_SELECT}) public @interface Mode { } public static final int MODE_SINGLE_SELECT = 0; public static final int MODE_MULTIPLE_SELECT = 1; private final RecyclerView.Adapter<?> mAdapter; private final OnModeChangedListener mModeChangedListener; @Nullable private final OnSelectionChangedListener mSelectionChangedListener; @Mode public int mMode = MODE_SINGLE_SELECT; public Set<Integer> mSelectedItems = new HashSet<>(); public MultiSelectionHelper(@NonNull RecyclerView.Adapter<?> adapter, @NonNull OnModeChangedListener modeChangedListener, @Nullable OnSelectionChangedListener selectionChangedListener) { mAdapter = adapter; mModeChangedListener = modeChangedListener; mSelectionChangedListener = selectionChangedListener; } /** * Switch to a different selection mode. Mode switches can also occur automatically if onLongClick() is called. */ public void setMode(@Mode int mode) { if (mMode == mode) { return; } mMode = mode; mModeChangedListener.onModeChanged(mode); } /** * Returns the current selection mode */ @MultiSelectionHelper.Mode public int getMode() { return mMode; } /** * To be invoked when an item in the list is clicked (even when not in multi selection mode). * * @return true if the click was handled and nothing further should be done, false if it should be handled as a normal (not selection) click. */ public boolean onClick(int position) { if (mMode == MODE_MULTIPLE_SELECT) { if (mSelectedItems.contains(position)) { mSelectedItems.remove(position); if (mSelectedItems.isEmpty()) { setMode(MODE_SINGLE_SELECT); } } else { mSelectedItems.add(position); } notifySelectionChanged(position); return true; } return false; } /** * To be invoked when an item in the list is long-clicked (even when not in multi selection mode). * * @return true if the click was handled and nothing further should be done, false if it should be handled as a normal (not selection) click. */ public boolean onLongClick(int position) { if (mMode == MODE_SINGLE_SELECT) { mSelectedItems.add(position); notifySelectionChanged(position); setMode(MODE_MULTIPLE_SELECT); return true; } return false; } /** * Clears the information about which items are selected. Should be called when we are done selecting items (e.g. if action is taken or selection is cancelled) */ public void clearSelectedPositions() { mSelectedItems.clear(); mAdapter.notifyDataSetChanged(); setMode(MODE_SINGLE_SELECT); } /** * Returns whether or not a certain position is selection */ public boolean isPositionSelected(int position) { return mSelectedItems.contains(position); } /** * Returns a set containing the positions of all selected items */ @NonNull public Set<Integer> getSelectedItems() { return mSelectedItems; } private void notifySelectionChanged(int position) { if (mSelectionChangedListener != null) { mSelectionChangedListener.onSelectionChanged(mSelectedItems.size()); } mAdapter.notifyItemChanged(position); } /** * Callback interface for notifying when the selection mode changes */ public interface OnModeChangedListener { /** * Invoked when the selection mode changes */ void onModeChanged(@Mode int multiSelect); } /** * Callback interface for notifying when the selection changes */ public interface OnSelectionChangedListener { /** * Invoked when the number of selected items change * * @param count the number of selected items */ void onSelectionChanged(int count); } }