List UI is a very common thing in Android Applications. Pretty much every single app has a list of something they want to display.RecyclerView Selection is a library that will allow you to handle item selection a lot easier. It will help you handle motion events and touch events, and convert them into selection in the RecyclerView.This is a fairly flexible library where it allows creating, modifying, inspecting, and monitoring changes to items in a RecyclerView list. It enables users to select items in RecyclerView list using touch or mouse input.

To add selection support to a RecyclerView instance, follow these steps:

1.Add Dependency


As you can imagine, you add the new dependency to your build.gradle file. An important thing to note here is that we are using the new AndroidX artifact.
Add following dependency to your app’s build.gradle file.28.0.0-alpha is a pre-release version to support the Android P developer preview.

dependencies {                                                                                                                                                                                                         
    implementation 'androidx.recyclerview:recyclerview:1.0.0-beta01'
    implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
    
    implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha1'
    implementation 'androidx.recyclerview:recyclerview-selection:1.0.0-beta01'
    ....
}

For the setup what you need to do is you want to create a new adapter and important thing is that it uses stable IDs.There is no selection code just yet. You need just basic RecyclerView setup.

2.Implement ItemKeyProvider


Developers must decide on the key type used to identify selected items. There are three key types: Parcelable, String, and Long.ItemKey provider conjunction of stable IDs. It will allow for a quick mapping between the IDs and the items that will handle the selection by the selection library.

public class MyItemKeyProvider extends ItemKeyProvider {
    private final List<Item> itemList;

    public MyItemKeyProvider(int scope, List<Item> itemList) {
        super(scope);
        this.itemList = itemList;
    }

    @Nullable
    @Override
    public Object getKey(int position) {
        return itemList.get(position);
    }

    @Override
    public int getPosition(@NonNull Object key) {
        return itemList.indexOf(key);
    }
}

3.Implement ItemDetails


ItemDetails implementation provides the selection library with access to information about a specific RecyclerView item. This class is a key component in controling the behaviors of the selection library in the context of a specific activity.

public class MyItemDetail extends ItemDetailsLookup.ItemDetails {
    private final int adapterPosition;
    private final Item selectionKey;

    public MyItemDetail(int adapterPosition, Item selectionKey) {
        this.adapterPosition = adapterPosition;
        this.selectionKey = selectionKey;
    }

    @Override
    public int getPosition() {
        return adapterPosition;
    }

    @Nullable
    @Override
    public Object getSelectionKey() {
        return selectionKey;
    }
}

4.Implement ItemDetailsLookup


ItemDetailsLookup enables the selection library to access information about RecyclerView items given a MotionEvent. It is effectively a factory for ItemDetails instances that are backed up by a RecyclerView.ViewHolder instance.

It is actually a fairly simple class. You need to overwrite only one method. That returns the position and the selection key for the item that is for the given motion event.

public class MyItemLookup extends ItemDetailsLookup {

    private final RecyclerView recyclerView;

    public MyItemLookup(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
    }

    @Nullable
    @Override
    public ItemDetails getItemDetails(@NonNull MotionEvent e) {
        View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
        if (view != null) {
            RecyclerView.ViewHolder viewHolder = recyclerView.getChildViewHolder(view);
            if (viewHolder instanceof ItemListAdapter.ItemListViewHolder) {
                return ((ItemListAdapter.ItemListViewHolder) viewHolder).getItemDetails();
            }
        }

        return null;
    }
}

The Selection library calls getItemDetails() when it needs access to information about the area. Your implementation must negotiate ViewHolder lookup with the corresponding RecyclerView instance and the subsequent conversion of the ViewHolder instance to an ItemDetailsLookup.ItemDetails instance.

public class ItemListAdapter extends RecyclerView.Adapter<ItemListAdapter.ItemListViewHolder> {
   ....
   public class ItemListViewHolder extends RecyclerView.ViewHolder implements ViewHolderWithDetails {
        ...
        @Override
        public ItemDetailsLookup.ItemDetails getItemDetails() {
            return new MyItemDetail(getAdapterPosition(), itemList.get(getAdapterPosition()));
        }
    }
}

The adapter, as I said again, nothing super exciting. The only important thing here is that we’re doing stable IDS. This allows for a consistent mapping from ID to the item.

5.Reflect Selected State


When the user selects an item the library will record that in SelectionTracker then notify RecyclerView that the state of the item has changed.The item must then be updated to reflect the new selection status. Without this, the user will not see that the item has been selected.

Update the styling of the view to represent the activated status with a color state list.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/selected_background" android:state_activated="true" />
    <item android:drawable="@android:color/white" />
</selector>

item_list.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    ...
    android:background="@drawable/item_background">

    ....
</android.support.constraint.ConstraintLayout>

RecyclerView has no default selection mechanism. You have to handle it in onBind.There you might want to change the background of a view or you can use it by setting activated state to get activates state working. You use background for the view and that background is a selectable drawable that has an activated state which will allow to indicate to the user that the item has been selected.

public class ItemListAdapter extends RecyclerView.Adapter<ItemListAdapter.ItemListViewHolder> {

    ...
    private SelectionTracker selectionTracker;

    public void setSelectionTracker(SelectionTracker selectionTracker) {
        this.selectionTracker = selectionTracker;
    }
    
    ....

    @Override
    public void onBindViewHolder(@NonNull ItemListViewHolder holder, int position) {
        Item item = itemList.get(position);

        holder.bind(item, selectionTracker.isSelected(item));

    }

    public class ItemListViewHolder extends RecyclerView.ViewHolder implements ViewHolderWithDetails {
        TextView itemId, itemName, itemPrice;

        ....

        public final void bind(Item item, boolean isActive) {
            itemView.setActivated(isActive);
            ....
        }
    }
}

6.Add Contextual Actions


ActionModes used to provide alternative interaction modes and replace parts of the normal UI until finished. Action modes include text selection and contextual actions.

public class ActionModeController implements ActionMode.Callback {

    private final Context context;
    private final SelectionTracker selectionTracker;

    public ActionModeController(Context context, SelectionTracker selectionTracker) {
        this.context = context;
        this.selectionTracker = selectionTracker;
    }

    @Override
    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
        return false;
    }

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

    @Override
    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
        return false;
    }

    @Override
    public void onDestroyActionMode(ActionMode actionMode) {
        selectionTracker.clearSelection();
    }
}

7.Build SelectionTracker


Now, what we need to set up is the selection tracker, which actually is the actual machinery behind the scenes, and we pass in RecyclerView, the key provider both of these that we just created.
In order to build a SelectionTracker instance, your app must supply the same Adapter that you used to initialize RecyclerView to SelectionTracker.Builder.

   itemListView.setAdapter(mAdapter);

   selectionTracker = new SelectionTracker.Builder<>(
          "my-selection-id",
           itemListView,
           new MyItemKeyProvider(1, itemList),
           new MyItemLookup(itemListView),
           StorageStrategy.createLongStorage()
   )
            
   .withOnDragInitiatedListener(new OnDragInitiatedListener() {
          @Override
          public boolean onDragInitiated(@NonNull MotionEvent e) {
                  Log.d(TAG, "onDragInitiated");
                  return true;
          }
      }).build();
   
     mAdapter.setSelectionTracker(selectionTracker);

Register a SelectionTracker.SelectionObserver to be notified when the selection changes. When a selection is first created, start ActionMode to represent this to the user, and provide selection specific actions.

    selectionTracker.addObserver(new SelectionTracker.SelectionObserver() {
        @Override
        public void onSelectionChanged() {
             super.onSelectionChanged();
             if (selectionTracker.hasSelection() && actionMode == null) {
                   actionMode = startSupportActionMode(new ActionModeController(MainActivity.this, 
                                selectionTracker));              setMenuItemTitle(selectionTracker.getSelection().size());
             } else if (!selectionTracker.hasSelection() && actionMode != null) {
                    actionMode.finish();
                    actionMode = null;
             } else {
                    setMenuItemTitle(selectionTracker.getSelection().size());
             }
             Iterator<Item> itemIterable = selectionTracker.getSelection().iterator();
             while (itemIterable.hasNext()) {
             Log.i(TAG, itemIterable.next().getItemName());
         }
      }
  });

8.Activity lifecycle events


In order to preserve state you must handling of Activity lifecycle events. your app must call the selection tracker’sonSaveInstanceState() and onRestoreInstanceState() methods from the activity’s onSaveInstanceState()and onRestoreInstanceState() methods respectively.

private SelectionTracker mTracker;

 public void onCreate(Bundle savedInstanceState) {

   if (savedInstanceState != null) {
      mTracker.onRestoreInstanceState(savedInstanceState);
   }
 }

 protected void onSaveInstanceState(Bundle outState) {
     super.onSaveInstanceState(outState);
     mTracker.onSaveInstanceState(outState);
 }

Download this Project from GitHub