I am new on kotlin android. I have created the adapter for recyclerview. But I am not able to perform a click event for each recyclerview item. I need the explanation with the reference code.
Kindly help me to do this.
Thanks in advance.
Here is my code for your reference.
class CustomAdapter(val readerList: ReaderResponse, mainActivity:
MainActivity,val btnlistener: BtnClickListener) :
RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
companion object {
var mClickListener: BtnClickListener? = null
}
override fun onCreateViewHolder(viewgroup: ViewGroup, index: Int): ViewHolder
{
val view=LayoutInflater.from(viewgroup?.context).inflate(R.layout.reader_list,viewgroup,false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return readerList.results.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
mClickListener = btnlistener
val item = readerList
val reader:ReaderData = readerList.results[position]
/*p0?.imageview?.text=reader.readerIcon*/
holder?.reader_status?.text=reader.readerStatus
holder?.ward_name?.text=reader.wardName
holder?.reader_id?.text=reader.readerID
holder?.reader_name?.text=reader.readerName
holder?.reader_location?.text=reader.readerLocation
if (reader.readerStatus.toLowerCase().equals("yes")){
holder.reader_name.setTextColor(Color.parseColor("#24a314"))
}else if (reader.readerStatus.toLowerCase().equals("no")){
holder.reader_name.setTextColor(Color.parseColor("#f4312d"))
holder.warning.setVisibility(View.VISIBLE)
}
}
class ViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView) {
val imageview = itemView.findViewById(R.id.imageview) as Button
val reader_name = itemView.findViewById(R.id.reader_name) as TextView
val reader_location = itemView.findViewById(R.id.floor_no) as TextView
val ward_name = itemView.findViewById(R.id.ward_name) as TextView
val reader_id = itemView.findViewById(R.id.reader_id) as TextView
val reader_status = itemView.findViewById(R.id.reader_status) as TextView
val warning=itemView.findViewById(R.id.warning) as Button
}
open interface BtnClickListener {
fun onBtnClick(position: Int)
}
}
You could use the following approach. This is taken from this blog by Antonio Leiva
Assuming your data class is ReaderData
class CustomAdapter(val readers: List, val listener: (ReaderData) -> Unit) {
/* Other methods */
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
/*...*/
holder.imageview.setOnClickListener {
listener(readers[position])
}
}
}
Now in your Activity or Fragment
recyclerview.adapter = CustomAdapter(readersList) { readerData ->
Log.i(TAG, "${readerData.readerID} clicked")
}
The idea is you pass a lambda which will be executed when your desired item is clicked.
You just need to implement BtnClickListener in the corresponding Activity in which this adapter is initialized. Once you have implemented the BtnClickListener it would override the function onBtnClick in the activity.
The only thing you need to do in the adapter is to initialize the onClickListener on the element you need and in that method just call imageview.setOnClickListener { mClickListener?.onBtnClick(position) }. It would send the position back in activity and you can perform your specific task there. For example I have implemented the ClickListener in one Activity and printed the log there it works fine. Below is the demo code for it.
class Main2Activity : AppCompatActivity(), CustomAdapter.BtnClickListener {
override fun onBtnClick(position: Int) {
Log.d("Position", position.toString())
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayout.VERTICAL, false)
val readerResponseList = ArrayList<YourModelClassName>()
val adapter = CustomAdapter(readerResponseList,this,this)
recyclerView.adapter = adapter
}
Hope it Helps.
Related
Please tell me how to transfer the ID (position) of the view element on recyclerview to another class?
class CardAdapter : RecyclerView.Adapter<CardAdapter.CardViewHolder>(), View.OnClickListener {
private var cardList = ArrayList<Card>()
private lateinit var card: Card
class CardViewHolder(
val binding: FragmentCardBinding
) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = FragmentCardBinding.inflate(inflater, parent, false)
return CardViewHolder(binding)
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
card = cardList[position]
..
}
override fun getItemCount(): Int = cardList.size
override fun onClick(v: View) {
when (v.id) {
R.id.root_card_template -> {
val intent = Intent(v.context, ProductActivity::class.java)
// need put id into ProductActivty
intent.putExtra("item", card.id)
v.context.startActivity(intent)
}
}
}
At the moment, it only passes the ID of the last generated element. For some reason there is almost no information on the Internet on this score
Just create an interface and implement that in the calling activity. While creating an instance of your adapter inside the activity, pass that interface along and on the click event of the view in the adapter class, call the interface's method with the data that you want to pass back to the activity.
interface OnItemClickListener{
fun onClick(pos: Int)
}
class YourActivity: AppCompatActivity(), OnItemClickListener {
override fun onStart() {
super.onStart()
//Create an instance of your adapter and pass the interface.
// Here #this context is being passed as Activity is implementing the interface.
val cardListAdapter = CardListAdapter(this)
}
override fun onClick(pos: Int) {
//Add your logic
}
}
// Then in your adapter class
class YourAdapter(private val itemClickListener: OnItemClickListener) :
RecyclerView.Adapter<CardListAdapter.CardViewHolder>() {
//Your code
override fun onBindViewHolder(holder: CardListAdapter.CardViewHolder, position: Int) {
yourView.setOnClickListener {
itemClickListener.onClick(position)
}
}
}
first you need to creat an interface
interface OnItemListener {
fun onItemSelect(position: Int)
}
then in your class that calls the recyclerview adapter, pass it to your recyclerView Adapter like this
var cardAdapter = CardAdapter(object :
OnItemListener {
override fun onItemSelect(position: Int) {
// you can handle your data here
// your position that you passed comes here
}
})
var layoutManager = GridLayoutManager(context, 2)
yourRecyclerViewId.adapter = cardAdapter
yourRecyclerViewId.layoutManager = layoutManager
finally in your adapter do like this
class CardAdapter(
private val onItemListener: OnItemListener
)
: RecyclerView.Adapter<CardAdapter.CardViewHolder>(), View.OnClickListener {
private var cardList = ArrayList<Card>()
private lateinit var card: Card
and in your on click event in the adapter call it as below:
onItemListener.onItemSelect(yourPosition)
I'm really confused about how the Kotlin lambdas work, specifically with click listeners. I had something that was working to do a single ViewModel function in my MainFragment but now I want multiple buttons on my adapter that do different things. At first I thought I would just have to pass all the necessary information including IDs for the different buttons to the callback then do a switch statement in my main fragment that does the appropriate ViewModel functions. As soon as I changed my input parameters the adapter no longer accepted my OnClickListener argument.
First I'll show the old OnClickListener that was working.
ItemAdapter
class ItemAdapter(private val context: Context, private val onClickListener: OnClickListener) : ListAdapter<SongWithRatings, ItemAdapter.SongViewHolder>(SongsComparator())
{
lateinit var isVisible: BooleanArray
override fun onCurrentListChanged(
previousList: List<SongWithRatings>,
currentList: List<SongWithRatings>
) {
super.onCurrentListChanged(previousList, currentList)
if(previousList.size != currentList.size) {
isVisible = BooleanArray(itemCount)
isVisible.fill(element = false)
}
}
class SongViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val textView: TextView = view.findViewById(R.id.item_title)
val textViewBpm: TextView = view.findViewById(R.id.bpm)
val lastPlayed: TextView = view.findViewById(R.id.lastPlayed)
//new rating code 11/27/2021
val ratingBar: SeekBar = view.findViewById(R.id.ratingBar)
val ratingLabel: TextView = view.findViewById(R.id.ratingLabel)
val button: Button = view.findViewById(R.id.submitRating)
//expandable view 3/27/2022
val titleView: LinearLayout = view.findViewById(R.id.titleView)
val expand: ConstraintLayout = view.findViewById(R.id.expand)
//fragment launch buttons
val moreButton: Button = view.findViewById(R.id.moreButton)
val rateButton: Button = view.findViewById(R.id.rateButton)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder {
// create a new view
val adapterLayout = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return SongViewHolder(adapterLayout)
}
override fun onBindViewHolder(holder: SongViewHolder, position: Int) {
val item = getItem(position)
//temporary code for initial rating
val initialRating = item.recentPerformanceRating()
var newRating = 0
holder.textView.text = item.song.songTitle
holder.textViewBpm.text = context.resources.getString(R.string.BPM,item.song.bpm)
holder.ratingLabel.setBackgroundColor(getStatusColor(item.recentPerformanceRating()))
//holder.imageView.setImageResource(item.imageResourceID)
holder.ratingLabel.text = context.resources.getString(R.string.Rating, initialRating )
holder.lastPlayed.text = item.lastPlayedString()
//rating bar functionality
holder.ratingBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, rating: Int, fromUser: Boolean) {
holder.ratingLabel.text = context.resources.getString(R.string.Rating, rating)
newRating = rating
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
//Performance Rating button functionality
holder.button.setOnClickListener{
onClickListener.onClick(item.song.songTitle, newRating)
}
holder.moreButton.setOnClickListener { }
class OnClickListener(val clickListener: (songTitle: String, newRating: Int) -> Unit) {
fun onClick(songTitle: String,newRating: Int) = clickListener(songTitle, newRating)
}
}
From MainFragment
val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_view)
val adapter = ItemAdapter(requireContext(),
ItemAdapter.OnClickListener { songTitle, newRating ->
songViewModel.insertRating( Rating(System.currentTimeMillis(),songTitle,songViewModel.artistName, newRating )) }
)
Like I said, this all worked fine until I tried to use the OnClickListener with a SongsWithRatings parameter.
Am I even close here or do I have to redo my whole interface between the adapter, fragment and ViewModel?
You just have to make use of Interface for the purpose of providing listeners to the Fragment .
Step 1: Create an Interface Class.
interface ItemClickListener{
//You can include the parameters into the functions which you wish to be associated with the button. Suppose I want Title on Click of more Button, then I will pass it as a parameter
fun onButtonClick(val item : SongsWithRating)
fun onMoreButtonClicked(val title : String)
}
Step 2 : Create a listener variable in your adapter and call the functions in the onClick function of the respective buttons
class ItemAdapter(private val context: Context) : ListAdapter<SongWithRatings, ItemAdapter.SongViewHolder>(SongsComparator())
{
var listener : ItemClickListener ?= null
override fun onBindViewHolder(holder: SongViewHolder, position: Int) {
val item = getItem(position)
//Calling buttonClick and passing the function defined in the interface
along with the parameters
holder.button.setOnClickListener{
listener?.onButtonClick(item)
}
//Similarly for morebutton
holder.moreButton.SetOnClickListener{
listener?.onMoreButtonClicked(item.song.songTitle)
}
}
}
Step 3 : Now the final Step : Go to the fragment wherein the recyclerView is implemented
//Extent the Fragment with the Interface and override the methods
class Fragment : Fragment(), ItemClickListener{
//define Adapter and attach the listener
val adapter = ItemAdapter(requireContext()
adapter.listener = this
}
You are good to go
I am trying do I return the result of the position from my Recyclerview Adapter, but I can't call the " adapter.setOnItemClickListener(this)" from MainActivity.kt? I get the error "Unresolved reference: setOnItemClickListener"?
I've had to post a new thread as I can't get all the code to be shown - after taking quite a few hours failing on this I am losing the will to live :-(
Adapter.kt
class UsersAdapter(
private val users: ArrayList<User>
) : RecyclerView.Adapter<UsersAdapter.DataViewHolder>() {
class DataViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
fun bind(user: User) {
itemView.textViewUserName.text = user.name
Glide.with(itemView.imageViewAvatar.context)
.load(user.avatar)
.into(itemView.imageViewAvatar)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
DataViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.item_layout, parent, false)
)
override fun getItemCount(): Int = Int.MAX_VALUE
override fun onBindViewHolder(holder: DataViewHolder, position: Int){
val pos = position % users.size
holder.bind(users[pos])
Log.d(Constraints.TAG, "onBindViewHolder:" + pos)
}
lateinit var listener: OnItemClickListener
public interface OnItemClickListener {
fun getAdapterPosition(position : Int )
}
public fun setOnItemClickListener(listener: OnItemClickListener) {
this.listener= listener
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var adapter: ConcatAdapter
lateinit var userVerticalAdapter: UsersAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupDataInRecyclerView()
}
//override
public fun getAdapterPosition(position : Int ){
// required value is in the position variable
Log.d(Constraints.TAG, "This is where it is going!" )
}
private fun setupDataInRecyclerView() {
recyclerView.layoutManager = LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, false)
userVerticalAdapter = UsersAdapter(DataSource.getUser())
val listOfAdapters = listOf(userVerticalAdapter)
adapter = ConcatAdapter(listOfAdapters)
//This errors with "Unresolved reference: setOnItemClickListener"
adapter.setOnItemClickListener(this)
recyclerView.adapter = adapter
recyclerView.scrollToPosition(Int.MAX_VALUE/2)
ItemSnapHelper().attachToRecyclerView(recyclerView)
}
public fun ShowWhatRecieved(isitthere: Int){
ShowMeIt.text = isitthere.toString()}
}
Revised and Updated MainActvity.kt
class MainActivity : AppCompatActivity(), UsersAdapter.OnItemClickListener {
lateinit var adapter: ConcatAdapter
lateinit var userVerticalAdapter: UsersAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupDataInRecyclerView()
}
override public fun getAdapterPosition(position : Int ){
Log.d(Constraints.TAG, "This is where it is going!" )
}
private fun setupDataInRecyclerView() {
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
userVerticalAdapter = UsersAdapter(DataSource.getUser())
val listOfAdapters = listOf(userVerticalAdapter)
adapter = ConcatAdapter(listOfAdapters)
adapter.setOnItemClickListener(this)
recyclerView.adapter = adapter
recyclerView.scrollToPosition(Int.MAX_VALUE/2)
ItemSnapHelper().attachToRecyclerView(recyclerView)
}
public fun ShowWhatRecieved(isitthere: Int){
ShowMeIt.text = isitthere.toString()
}
}
You are passing this as argument it will pass the context but in the parameter of function setOnItemClickListener in adapter you have defined listener of OnItemClickListener type. So you need to pass the argument of OnItemClickListener type.
You should implement the OnItemClickListener at your activity and you will be able to use this keyword. But you also can make an anonymous implementation of OnItemClickListener interface when you try to send.
But I would prefer the option 1. To implement the OnItemClickListener to your activity, implement all methods of that interface and than you can use this keyword when you need to pass OnItemClickListener anywhere in that class.
You have not implemented interface in your as below -
class MainActivity : AppCompatActivity(), RecycleViewCartAdapter.OnItemClickListener
Here is the problem mate.
Despite all your effort by creating an interface and implementing it inside your recycler-view, I suggest you use lambda instead of interface since you are writing Kotlin, and writing a lambda instead of an interface is a cleaner approach in Kotlin for ClickListener.
Create a lambda parameter both for your adapter and your viewholder. since you want to pass the position of a clicked item so your lambda must have an Int input and nothing as output so it would look like this: listener: (Int) -> Unit.
The rest of your code would be something like this, you get the idea I just edited important parts:
class UsersAdapter(private val users : ArrayList<CrashlyticsReport.Session.User>,
private val listener : (Int) -> Unit) :
RecyclerView.Adapter<UsersAdapter.DataViewHolder>() {
class DataViewHolder(itemView : View, private val listener : (Int) -> Unit) :
RecyclerView.ViewHolder(itemView) {
init {
itemView.setOnClickListener { listener(adapterPosition) }
}
fun bind(user : CrashlyticsReport.Session.User) {
itemView.textViewUserName.text = user.name
Glide.with(itemView.imageViewAvatar.context).load(user.avatar).into(itemView.imageViewAvatar)
}
}
}
then in your activity you can write:
userVerticalAdapter = UsersAdapter(DataSource.getUser()) {
//clicklistener event here
}
or:
userVerticalAdapter = UsersAdapter(DataSource.getUser(),this::ShowWhatRecieved)
also since you're using ConcatAdapter it's better to use bindingAdapterPosition instead of adapterPosition.
What's the objective
Im currently working on an app which has a RecyclerView for the Settings menu. This menu serves to load other fragments. So i needed to implement an OnItemClick function: for this, i followed this video.
What's the probelm
Following the given tutorial, Android Studio flags val adapter = adapterSettings(settingsList), saying No value passed for parameter 'listener'. I suppose that im missing something, since without the code written in the tutorial, the RecyclerView works.
So, am i missing something? Are there any ways to fix this in an easy and clean way?
Code:
activitySettings.kt
class ndActSettings : AppCompatActivity(), adapterSettings.OnItemClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.ndactivity_settings)
topToolbarBack.setNavigationOnClickListener {
finish()
}
var settingsList = listOf(
dataItemsSettings(getString(R.string.look), getString(R.string.lookdescription), R.drawable.ic_colored_color_lens),
dataItemsSettings(getString(R.string.reproduction), getString(R.string.reproductiondescription), R.drawable.ic_colored_view_carousel),
dataItemsSettings(getString(R.string.images), getString(R.string.imagesdscription), R.drawable.ic_colored_image),
dataItemsSettings(getString(R.string.audio), getString(R.string.audiodescription), R.drawable.ic_colored_volume_up),
dataItemsSettings(getString(R.string.about), getString(R.string.aboutdescription), R.drawable.ic_colored_info)
)
val adapter = adapterSettings(settingsList) //ERROR HERE!
rvSettings.adapter = adapter
rvSettings.layoutManager = LinearLayoutManager(this)
}
override fun OnItemClick(position: Int) {
//TODO
}
}
adapterSettings.kt
class adapterSettings(
var settingsList: List<dataItemsSettings>,
var listener: OnItemClickListener
) : RecyclerView.Adapter<adapterSettings.SettingsViewHolder>() {
inner class SettingsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.setOnClickListener(this)
}
override fun onClick(p0: View?) {
val position : Int = adapterPosition
if (position != RecyclerView.NO_POSITION) {
listener.OnItemClick(position)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingsViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_settings, parent, false)
return SettingsViewHolder(view)
}
override fun getItemCount(): Int {
return settingsList.size
}
override fun onBindViewHolder(holder: SettingsViewHolder, position: Int) {
holder.itemView.apply {
rvTitle.text = settingsList[position].stringTitle
rvDescription.text = settingsList[position].stringDescription
rvIcon.setImageResource(settingsList[position].itemIcon)
}
}
interface OnItemClickListener {
fun OnItemClick(position: Int)
}
}
The constructor of class adapterSettings is expecting two parameters
class adapterSettings(
var settingsList: List<dataItemsSettings>,
var listener: OnItemClickListener
)
However, you are instantiating the object with one parameter only:
val adapter = adapterSettings(settingsList)
So, you have to add a second parameter.. An object that implements OnItemClickListener. Since you activity already implements that interface, you can send the activity as second parameter:
val adapter = adapterSettings(settingsList, this)
I'm new in Android dev.
In my code i don't using onClick method, but i using setOnClickListener and Callback. The main problem that is in this way i don't know how to get the position of the item in RecyclerView.
Here is my Adapter:
class TestAdapter(val test : ArrayList<Test>, private val testAdapterCallback: (Test, Int)->Unit) : RecyclerView.Adapter<TestAdapter.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.test_view_item, parent, false)
return ViewHolder(v)
}
override fun getItemCount(): Int {
return test.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val num : Test = test[position]
holder.textView.text = num.id.toString()
holder.cardView.setTag(position)
holder.cardView.setOnClickListener(){
testAdapterCallback(num)
}
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val cardView = itemView.findViewById<CardView>(R.id.testCardView)
val textView = itemView.findViewById<TextView>(R.id.testTextView)
}
}
I have added second parametr into callback but i don't know how i must change my adapter's inicializations in this peace of code:
val adapter = TestAdapter(list) { item ->
testAdapterItemClick(item)
}
In activity i'm using this method :
private fun testAdapterItemClick(item: Test) {}
Please, help me to check the position of the choosen element. I need it later.
Thanks in advance)
P.S. Sorry for my English
Add the position as parameter in the callback.
So, instead of: private val testAdapterCallback: (Test)->Unit
Use: private val testAdapterCallback: (Test, Int)->Unit.
This way you can pass the position in the callback.
holder.cardView.setOnClickListener(){
testAdapterCallback(num, position)
}
In your activity:
val adapter = TestAdapter(list) { item, position ->
testAdapterItemClick(item)
}
Create interface for your OnClickListener
interface OnClickListener{
fun clickItem(test: Test, index: Int)
}
Pass listener to your adapter like below.
class TestAdapter(
var test : ArrayList<Test>?,
val clickListener: OnClickListener,
var mActivity: Activity
) :
RecyclerView.Adapter<TestAdapter.MyViewHolder>() {
}
Now in your onBindViewHolder add click listener.
var mtest= test !!.get(i)
holder.cardView.setOnClickListener {
clickListener.clickItem(mtest, i)
}
Implement Listener to your activity and initialize it.
this.mOnClickListener = this
Pass listener to your adapter where you passing the arraylist.
mTestAdapter = TestAdapter(arrayList, mOnClickListener ,mActivity)
You'll get the position in your activity override method.
override fun editItem(mTest: Test, index: Int) {
if (mTest!= null) {
}
}