Initial Setup
We will begin by creating a new android project by clicking the New Project button. Choose Empty Activity and then click the Next button.
On the next screen, set the project name to whatever you want. We have set it to RecyclerSelection. Make sure that the language is Kotlin and that the minimum SDK value is 23.
The ability to select any items and control their visual representation along with other things like selection eligibility and how many items can be selected is implemented through the recyclerview-selection library. Therefore, you need to open your build.gradle file and add the following dependencies:
Creating Our List
We will now write some XML and kotlin code to display a list of to-do tasks in our app. Create a file called todo_task.xml by navigating to Layout > New > Layout Resource File. It should have the following XML:
I have gone ahead and used a CardView
widget in my layout file. However, you can just use a LinearLayout
if you want, without wrapping it inside a CardView
. This layout file defines how individual items in our to-do task will appear.
Now add the following XML to the activity_main.xml file.
The main layout file simply contains a RecyclerView
widget because that's all we need.
This time, our data class will be called TaskItem
and the adapter class will be called RVAdapter
class because it is just an adapter for our RecyclerView
.
At this point, our code is pretty much similar to the previous tutorial. Only the syntax is more concise. The RVAdapter
class creates and binds view holders for each task. You should consider reading our previous tutorial to learn the basics of using RecyclerView
if any of the above code looks confusing.
We have added a new method called getItemId()
that is supposed to generate a stable ID for any given item. This method uses the position of the item to generate this ID. This is acceptable here because we are not deleting or adding items to the list.
You should also add the following code to the MainActivity
class:
Run the application at this point and you will see a simple list of three to-do tasks.
Implementing the Selection Functionality
Long presses on any list item will not result in their selection at this point. However, that functionality is easy to add. We will begin by updating our RVAdapter
class to include a tracker.
Update the TaskViewHolder
class by adding a getItemDetails()
method as shown below:
The getItemDetails()
method returns an anonymous object that is extending ItemDetailsLookup.ItemDetails<Long>
. It overrides the getPosition()
and getSelectionKey()
methods to return the position and selection key of the item associated with our view holder.
It might sound confusing now but we will use the getItemDetails()
method later to determine which item was clicked by the user.
There is a getItmeId()
method in our RVAdapter
class. Add the following code above this method:
The code inside init
block is executed whenever RVAdapter
is instantiated. We use this block to explicitly state that all the adapter items will have stable IDs. In other words, the IDs won't change even if their position within the dataset changes.
We also define a tracker
property and use the setTracker()
method to set its value.
Now, update the onBindViewHolder()
method to have the following code:
The onBindViewHolder()
method binds data from the adapter's dataset to a view holder. In this case, it simply sets the text value for the title
and description
views in the first two lines. After that, it gets a reference to the parent CardView
of the concerned title.
Next, we call the let
function on our tracker object. It lets us execute a block of code but only if the calling object is not null
. We use this function to check if the item at the given position is selected to see if its color should be changed to light gray.
Now, we will write the code for our ItemLookup
class which will help our tracker determine which item was clicked by the user.
We are overriding the getItemDetails()
method of the ItemDetailsLookup
class inside our ItemLookup
class. The returned ItemDetails
object of this method contains the details about the item that was touched.
The MotionEvent
argument passed to getItemDetails()
is used to find the child view which was clicked. After that, we obtain a reference to the child's view holder and call its getItemDetails()
method. The getItemDetails()
method returns an ItemDetails
object which is ultimately returned by the getItemDetails()
method of the ItemLookup
class.
Updating the onCreate()
Method to Track Selections
At this point, we are ready to add the tracking functionality to the onCreate()
method of our MainActivity
class. Update your onCreate()
method so that it has the following code:
We initialize our tracker using the SelectionTracker.Builder
class. The constructor takes several arguments — a selection ID to uniquely identify the selection, the RecyclerView
where the selection is happening, a key provider, your item details lookup class, and a storage strategy.
You need to provide a storage strategy so that the active selection isn't lost whenever activity configuration changes. Our selection keys are of Long
type. Therefore, it is important that our storage strategy also creates a storage for Long
type.
Once the Builder
is ready, you can call its withSelectionPredicate()
method to specify how many items you want to allow the user to select. In order to support multi-item selection, as an argument to the method, you must pass the SelectionPredicate
object returned by the createSelectAnything()
method.
If the savedInstanceState
is not null
, we also use the let
function to call the onRestoreInstanceState()
method on our tracker object. This restores the selection state of the tracker from its saved state.
The selection tracker is not very useful unless it is associated with your adapter. Therefore, pass it to the adapter by calling the setTracker()
method. We have already defined this method in the previous section.
You will probably want to take different actions in your app depending on the number of selection items. We can use the addObserver()
method on our tracker
object to observe any changes in the selection. After that, we override the onSelectionChanged()
method to change the title and background color of the action bar. This lets the users know that there is an active selection in the list and how many items have been selected.
Saving the Activity State
Just like the onCreate()
method which is invoked when an activity is first created, there is a method called onSaveInstanceState()
that is called when the activity is about to be destroyed either due to a configuration change or to reclaim memory. We want to save the state of our activity and our selection at this point. Therefore, we will override this method using the following code and place it below the onCreate()
method.
How the Code Works Together
We have divided the code for the entire app in multiple parts to explain how it works. However, this can make it hard to figure out how to whole thing fits together. I will now explain how different activities and methods are connected together.
When a user launches the app, it creates an instance of the MainAcitivity
class and calls the onCreate()
method. All our initial setup is done inside the onCreate()
method. This includes initializing the user interface and setting up the RecyclerView
as well as its adapter with the help of the RVAdapter
class. The RVAdapter
class is needed to create and bind a view holder to each of our tasks.
The SelectionTracker
is also set up inside onCreate()
itself. We pass this tracker to the adapter using the setTracker()
method. The adapter relies on the SelectionTracker
to determine which tasks have been selected and updates their background accordingly.
The SelectionTracker
needs the ItemLookup
class to determine which item was clicked by the user. This class determines which item was clicked by the user. The SelectionTracker
updates its observer about any change in the selection state of different items. This observer was attached to our tracker when the activity was created, and it updates the action bar title and color based on the selected items.
You might have noticed that there are two getItemDetails()
methods. The first one is inside the TaskViewHodler
class and the second one is inside the ItemLookup
class. However, they both serve different purpose.
The getItemDetails()
method inside TaskViewHolder
returns information about the item associated with the view holder. However, the getItemDetails()
method inside ItemLookup
returns information about the item that was clicked. It does so by first calling the findChildViewUnder()
to determine which item is under the user touch event and ultimately calling the getItemDetails()
method of the TaskViewHodler
class.
The code inside the onSaveInstanceState()
method saves the state of the activity and the selection before it is destroyed by the system. We restore back this state later inside the onCreate()
method if the savedInstanceState
is not null
.
Final Thoughts
In this tutorial, you learned how to use the RecyclerView Selection addon library to add simple item selection support to a RecyclerView
widget. You also learned how to dynamically alter the appearance of selected items so that users can tell them apart from unselected ones.
No comments:
Post a Comment