- Adding OnClickListener to RecyclerView in Android
- YouTube Version
- An abstract view of what we are doing
- The Fragment methods that we are using.
- onCreate(Bundle,savedInstanceState)
- onCreateView(LayoutInflater, ViewGroup, Bundle)
- onViewCreated(View, Bundle)
- Creating our method.
- 1) Why is it nested inside the CustomAdapter class ?
- 2) why are we using an interface ?
- Implementing the Interface
- Passing access to our CustomAdapter
- Passing onNoteListener to onCreateViewHolder()
- Hooking up the ViewHolder object.
- RecyclerView Item Click Listener the Right Way
- Example Scenario
- Project Creation
- Data model
- Recyclerview Adapter and view holder
- Item Click the bad way
- Let us fix it
- Android RecyclerView onItemClickListener & getAdapterPosition(): A Better Way
- A lighter way to get that thing done smoothly.
- The Most Popular Way; The Problem
- Introverts Decoded: Unspoken Dilemma While In Love
- Things they never tell anyone else.
- And for some reasons you can not use Java8’s Lambda functions then go ahead as following way is still a better option for you.
- A Better Way; The Solution
Adding OnClickListener to RecyclerView in Android
This series is going to be dedicated to the basic of Android development. Join me and let us try to build and understand some cool stuff. All the resources I used to create this post can be found on ticketnote or HERE.
I also want to mention one final thing that I except from you the reader. Before reading this you should have a solid understanding of Android Fragments and the RecyclerView. I mention this because I will not be going over fragment transactions or how to implement a RecyclerView. Check out HERE for my fragments tutorial and HERE for my RecyclerView tutorial. If you do not understand these fundamentals then you may find it hard to follow along in this tutorial.
YouTube Version
An abstract view of what we are doing
- Basically what we are doing is defining a method passing the method to our CustomAdapter instance, then to the createViewHolder() method and finally to the ViewHolder instance. This method will get called when each individual ViewHolder object is clicked. We of course will provide a much more technical implementation but generally speaking that is what we are doing.
The Fragment methods that we are using.
- Now before we get into actually implementing the method I want to talk about the fragment methods we are going to use in our implementation. Examples of our fragment’s code are below:
Exit fullscreen mode
- This may seem like a lot of code but for now only focus on the onCreate(), onViewCreated() methods and the class constructor.
- Also if you are unfamiliar with the fragment lifecycle then I highly suggest that you read about it HERE. From this point on I will assume you have a solid understanding of fragments, the fragment lifecycle and recyclerviews
onCreate(Bundle,savedInstanceState)
- When a fragment is added to the Fragment Manager (fragment manager is responsible for organizing and calling methods in fragments) it enters the CREATED state of its lifecycle. This transition into the state triggers the onCreate() method. The method receives a Bundle which contains any previously saved state but initially the value is null. This method is also called before onCreateView()(which we do not have, more on that later) and called before onViewCreated()
- Now I am not a 100% sure why but in the example documentation HERE we are told to initialize data sets in this method. That means that this is the method we would normally make a server call to, or in our case set up an ArrayList.
onCreateView(LayoutInflater, ViewGroup, Bundle)
- This method is called when the view is going to be inflated(created). The documentation recommends that we only use this method for inflating the view and then move the logic to onViewCreated().
- The question you are probably asking your self is, «if this method is used for inflating the view then why don’t we have one?». We do have this method, it is just being called automatically for us. Proof of this is in the constructor:
Exit fullscreen mode
- When we make a super call of super(R.layout.example_fragment) we are invoking an alternative constructor within the Fragment class which will take the View that we provided it and automatically call onCreateView() for us.
onViewCreated(View, Bundle)
Exit fullscreen mode
This method gets called immediately after onCreateView() has returned and receives the inflated view from it. We can use that inflated view to set up our RecyclerView.
Now we can move on to creating the method that will get passed to our adapter and eventually the ViewHolder object.
Creating our method.
- First I am going to paste the code and then talk about the part pertaining to creating our method.
Exit fullscreen mode
- This is our entire custom adapter but the part we only care about is the interface OnNoteListener :
Exit fullscreen mode
- After seeing this interface, you probably have 2 questions, 1) why is it nested inside the CustomAdapter class and 2) why are we using an interface?
1) Why is it nested inside the CustomAdapter class ?
- Generally in Java we nest something inside of a class because it and the class have a close relation. This is the case for us because the interface really has no use outside of the context of our CustomAdapter class. This then leads us to our next question
2) why are we using an interface ?
- Before we can answer this question we first have to define what an interface is. A interface allows a class to be more formal about the behaviour a class provides. Interfaces form a contract between a class and the outside world. This contract is enforced at build time by the compiler. Basically when we use an interface on a class we are making a formal declaration on what methods it will be able to use and every class that implements the interface will also have those methods. So now that we know what an interface is, we have to understand why we would want to use an interface. Generally, it would be one of three reasons.
1) Unrelated classes need to have similar implementations.
2) You want to specify the behaviour of a particular data type but you are not concerned about who implements the behaviour.
3) You want to take advantage of multiple inheritance of a type.
- For our case it is 2, we want to specify a behaviour but we are not concerned about who implements the behaviour. Also, I want to point out that interfaces can also be used as reference data types. Same as a class.
Implementing the Interface
- Now if we look back at our Fragment class you should notice that we implement an interface:
Exit fullscreen mode
- When we implements an interface, we have to use CustomAdapter.onNoteListener because our interface is nested inside of our CustomAdapter class. Once we have implements our interface we must implement all of the interfaces methods, for us it is onNoteClick.
Exit fullscreen mode
This method is the method that we have been talking about. It is the method that we are going to pass around until it gets passed to the ViewHolder object. This method that is getting called when we click on the individual ViewHolder objects. We pass it an int to show that each method can call it with its own values. However, we are only using it to create and show a toast when clicked. Toast.makeText() is used to create a Toast, it takes 3 parameters: the context, the text to be shown and the duration that the toast will be viewed. toast.show() must be called in order for the toast to be shown.
Now remember that our goal is to simply pass a method to our ViewHoder object so that it gets called on every click. In order to do that we must to pass access to our method to the CustomAdapter object and then to the onCreateViewHolder() method and then finally to the ViewHolder object its self.
Passing access to our CustomAdapter
Exit fullscreen mode
- This is just us setting our Adapter, this.data is us using the instance variable called data as an argument for the the CustomAdapter. Now what is this ? Well, any time that ‘this’ is used inside of an instance method or constructor it references the current object. So when use this we are referencing public class ExampleFragment extends Fragment implements CustomAdapter.OnNoteListener .
- A quick reminder that we are also able to use a classes interface as its type when referencing it. I mention this because inside of our CustomAdapter constructor we are using the type of OnNoteListener and setting a global instance variable inside or our CustomAdapter class, see below:
Exit fullscreen mode
- However, I want to point out that just because we are referencing to it as an onNoteListener type, it is still technically our ExampleFragment class.
Passing onNoteListener to onCreateViewHolder()
- Now inside the onCreateViewHolder() method we call the constructor of the ViewHolder object.
Exit fullscreen mode
- This is still a normal onCreateViewHolder() method. However, notice that we are passing the global instance variable this.onNoteListener to the new ViewHolder() object. The ViewHolder instance will now get an inflated view as well as access to our onNoteClick method. Our viewHolder object now has access to the method that we defined earlier:
Exit fullscreen mode
Hooking up the ViewHolder object.
- In order for us to define a onClickListener on our ViewHolder object we need to implements View.OnClickListener:
Exit fullscreen mode
Notice the constructor and how we have an instance of onNoteListener and we are assigning it to a global reference variable inside of the ViewHolder class: this.onNoteListener = onNoteListener; . This will allow us to reference it later.
The most important part of this is the view.setOnClickListener(this) , without this method your ViewHolder objects will not be clickable. this is referring to the actual ViewHolder object. It is basically us saying that when the individual view is clicked we want the onClick method to be called.
Exit fullscreen mode
The code above is the last part of the puzzle. onClick() will get called whenever a ViewHolder object is called and with it we are calling our own method that we defined this.onNoteListener.onNoteClick(getAdapterPosition()) with the current position in the adapter.
That concludes how to make your RecyclerView clickable.
Источник
RecyclerView Item Click Listener the Right Way
Some Android developers wonder why Google created a view like Recyclerview without a click listener(given the fact that the deprecated ListView has an item click listener).
Because there is no standard way of setting a click listener, new developers tend to confuse on the right way of doing it. In this article, I will show you how to do it in a proper way using an example scenario.
Example Scenario
- There is a Recyclerview adapter with a Recyclerview with a list of items(Users in this case).
- What we want is that when an item is clicked, we get the item’s model(User) information and may be pass it to a second activity.
Project Creation
From Android Studio, create an empty android project(Select the Kotlin support option) and name your activity, MainActivity.
- Delete the default ‘Hello World’ TextView in activity_main.xml .
- Add recyclerview and cardview dependencies in app level build.gradle file as shown below.
Add recyclerview in activity_main.xml where you removed the textview as shown below.
Ok we are good to go. The assumption is that you have worked with the recyclerview(in Java) before and know how to create a recyclerview adapter.. Next, we create the model class containing username and phone.
Data model
Sweet Kotlin. Just the above line of code gives us access to a setter and getter under the hood and other methods like toString. Check more at https://kotlinlang.org/docs/reference/data-classes.html .
Next, we create our view holder and adapter. Pay attention here because it is the most important part of what this article addresses.
Recyclerview Adapter and view holder
- Create a new Kotlin file called RecyclerAdapter.kt
- Next we create our item_user.xml layout as follows
The item_user has two textviews which holds the name and phone.
Next we create our view holder. As usual, our view holder has a constructor with an itemView as parameter and we get a reference to our views from item_user layout .
Then we create our adapter with the list of users as a parameter. An adapter contains the list of users
Item Click the bad way
Note that in onBindView, we can have access to the the current clicked item and thus open a new activity from here and pass our data..
This will work perfectly but it is a bad practice because
- It is not a good practice opening an Activity from a viewholder context
- Note that onBindViewHolder is called as your views are populated during scrolling. Thus you will have numerous calls to setOnClickListener.
Let us fix it
The way that you should do is that you create an ItemClickListener interface with one method ontemClicked with parameter User.
- We then pass modify the Adapter’s constructor to take the users list and an OnItemClickListener interface
- We also modify the the ViewHolder to have a bind function which takes a user and itemClick interface as follows.
This is all we have to do. We just have to implement the interface in our MainActivity .
Источник
Android RecyclerView onItemClickListener & getAdapterPosition(): A Better Way
A lighter way to get that thing done smoothly.
Update 1: Included lambda functions as an alternative.
Update 2: Included a link to Implementation example App on GitHub.
TL;DR
Instead of creating a custom ItemClickListener interface to getAdapterPosition() inside your Activity/Fragment/View and end up creating multiple Anonymous Classes under the hood for each item. Better pass the RecyclerView. ViewHolder using view. setTag() inside your ViewHolder and then getAdapterPosition() (…and many other things) from view. getTag() i.e. ViewHolder’s object inside your Activity/Fragment/View.
Implementation example App on GitHub
The Most Popular Way; The Problem
I know you must have already found a way to get onItemClickListener() for your RecyclerView Adapter. But sometimes the way we think is not always the correct or a better way. Whenever anyone tries to find a way to getAdapterPosition() inside their Activity/Fragment/View. He or she comes across the well-known pattern of creating a custom interface with view and position as the parameters.
Later, we initialize the ItemClickListener instance using the public setter.
OR… by passing the ItemClickListener instance via constructor.
And finally we pass our view and adapter position to the listener.
And we know the rest of the part very well.
Most of the articles out there depict the same thing. But there is a problem.
When we say itemView .setOnClickListener(new View.OnClickListener()…
It creates an Anonymous inner class under the hood which implements our ItemClickListener listener.
And we end up creating and initializing multiple Anonymous inner classes under the hood just for a click listener.
I used to do the same. Because that is what we get to see all over the internet.
Introverts Decoded: Unspoken Dilemma While In Love
Things they never tell anyone else.
Update:
As suggested by Sachin Chandil in the comments section, we can use Java8’s Lambda functions to get rid of anonymous class creation at compile time. Lambda functions work with SAM types (interfaces with Single Abstract Method) and the reason it does not create anonymous inner class is the InvokeDynamic instruction, introduced in Java 7.
So with Lambdas, we can convert above function as below:
And for some reasons you can not use Java8’s Lambda functions then go ahead as following way is still a better option for you.
A Better Way; The Solution
But, recently I got a suggestion from a colleague to implement it in a better and lighter way. Let’s see how we can get adapter position, item id (…and many other things) inside our Activity/Fragment/View.
So, instead of creating new custom interface, we will use View.OnClickListener and initialize it in the way we used to do.
And we will pass the instance of ViewHolder via itemView to the listener using setTag().
In this way, we do not need to create Anonymous inner class as we can directly give the reference of onItemClickListener
Finally we can get adapter position and item id (…and many other things) inside our Activity/Fragment/View from the ViewHolder instance.
Simple! Isn’t it?
Let me know your views and suggestions about this in the comment section.
If you still have some doubts, here is the source code of the Implementation example App on GitHub .
Thanks for reading!
Rohit Surwase
If you like the article, clap clap clap 👏👏👏 as many times as you can.
LIKED SO MUCH! Medium allows up to 50 claps.
Also post a Tweet on Twitter.
Источник