RecyclerView itemClickListener en Kotlin

Estoy escribiendo mi primera aplicación en Kotlin después de 3 años de experiencia con Android. Simplemente confundido sobre cómo utilizar itemClickListener con un RecyclerView en Kotlin.

He intentado el enfoque del rasgo,

public class MainActivity : ActionBarActivity() { protected override fun onCreate(savedInstanceState: Bundle?) { // set content view etc go above this line class itemClickListener : ItemClickListener { override fun onItemClick(view: View, position: Int) { Toast.makeText(this@MainActivity, "TEST: " + position, Toast.LENGTH_SHORT).show() } } val adapter = DrawerAdapter(itemClickListener()) mRecyclerView.setAdapter(adapter) } trait ItemClickListener { fun onItemClick(view: View, position: Int) } } 

Eso me pareció muy networkingundante, así que probé el enfoque de class interna:

 inner class ItemClickListener { fun onItemClick(view: View, position: Int) { startActivityFromFragmentForResult<SelectExerciseActivity>(SELECT_EXERCISES) } } 

Y luego solo ajuste el oyente click de adaptador de esta manera:

 val adapter = WorkoutsAdapter(ItemClickListener()) 

Pero todavía no estoy satisfecho con esto porque creo que podría haber una manera mejor y más limpia. Intento esencialmente lograr algo como esto: RecyclerView onClick

¿Alguna sugerencia?

Terminó yendo con una variación de la respuesta aprobada

Definió la function en la actividad:

 val itemOnClick: (View, Int, Int) -> Unit = { view, position, type -> Log.d(TAG, "test") } 

Pasó al adaptador de esta manera:

 class ExercisesAdapter(val itemClickListener: (View, Int, Int) -> Unit) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { // other stuff up here val vhExercise = ExerciseVH(view) // view holder // on to the view holder through the extension function vhExercise.onClick(itemClickListener) } } 

Función de extensión por bucle en la respuesta aprobada a continuación.

 fun <T : RecyclerView.ViewHolder> T.onClick(event: (view: View, position: Int, type: Int) -> Unit): T { itemView.setOnClickListener { event.invoke(it, getAdapterPosition(), getItemViewType()) } return this } 

Tengo un enfoque un poco diferente. Puedes crear una extensión para tu ViewHolder

 fun <T : RecyclerView.ViewHolder> T.listen(event: (position: Int, type: Int) -> Unit): T { itemView.setOnClickListener { event.invoke(getAdapterPosition(), getItemViewType()) } return this } 

Entonces úselo en un adaptador como este

 class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>() { val items: MutableList<String> = arrayListOf() override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): MyViewHolder? { val inflater = LayoutInflater.from(parent!!.getContext()) val view = inflater.inflate(R.layout.item_view, parent, false) return MyViewHolder(view).listen { pos, type -> val item = items.get(pos) //TODO do other stuff here } } override fun onBindViewHolder(holder: MyViewHolder?, position: Int) { } override fun getItemCount(): Int { return items.size() } class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { } } 

Estoy trabajando con mis colegas de la biblioteca para proporcionar tales extensiones.

En caso de que alguien esté buscando una respuesta más sencilla , intenté lo siguiente, que es muy similar a la solución de AfzalivE :

En mi class de Adaptador, pasé el clickListener como parámetro. En onBindViewHolder , he usado setOnClickListener para llamar a clickListener y manejar el evento click.

MyAdapter.kt :

 class MyAdapter constructor(objects: ArrayList<MyObject>, val clickListener: (MyObject) -> Unit) : RecyclerView.Adapter<MyAdapter.Holder>() { private var mObjects : ArrayList<MyObject> = ArrayList<MyObject>() init { mObjects = objects } override fun onBindViewHolder(holder: Holder?, position: Int) { var item : MyObject = objects[position] // Calling the clickListener sent by the constructor holder?.containerView?.setOnClickListener { clickListener(item) } } // More code (ViewHolder definitions and stuff)... } 

Nota : Necesitaba una reference del contenedor de mi elemento de list (la vista raíz), que en este caso es containerView

Luego pasé mi object como parámetro sin necesidad de searchlo en una list nuevamente y manejarlo directamente en mi class Activity , en el momento en que establecí el adaptador:

MyActivity.kt :

 myRecyclerView?.adapter = MyAdapter(mObjects) { Log.e("Activity", "Clicked on item ${it.itemName}") } 

Actualizar

Si necesita get la position del elemento cliqueado, simplemente defínalo como parámetro en la callback y luego envíelo más tarde. Observe el val clickListener: (MyObject, Int) -> Unit continuación:

MyAdapter.kt

 class MyAdapter constructor(objects: ArrayList<MyObject>, val clickListener: (MyObject, Int) -> Unit) : RecyclerView.Adapter<MyAdapter.Holder>() { // Rest of the code... 

Luego, en onBindViewHolder() pasa la position al llamar al método de callback:

 override fun onBindViewHolder(holder: Holder?, position: Int) { var item : MyObject = objects[position] // Calling the clickListener sent by the constructor holder?.containerView?.setOnClickListener { clickListener(item, position) } } 

Y en MyActivity.kt , tendrá que cambiar la forma de configurar el adaptador para que pueda get el puesto. Me gusta esto:

 myRecyclerView?.adapter = MyAdapter(mObjects) { itemDto: MyObject, position: Int -> Log.e("MyActivity", "Clicked on item ${itemDto.someItemPropertyLikeName} at position $position") } 

Podrías probar algo como:

 public class MainActivity : ActionBarActivity() { protected override fun onCreate(savedInstanceState: Bundle?) { [...] val adapter = DrawAdapter(::onItemClick) [...] } } fun onItemClick(view: View, position: Int) { //Do work } 

y la conversión de SAM solo funciona como en Java 8, así que solo use una lambda:

 public class MainActivity : ActionBarActivity() { protected override fun onCreate(savedInstanceState: Bundle?) { [...] val adapter = DrawAdapter({view, position -> /*Do work*/ }) [...] } } 

En RecyclerView puede hacer clic en la vista inflada dentro de la class del usuario y llamarlo desde el método de callback onBindViewHolder, por ejemplo:

  class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val view = view val tv_message = view.tv_message val tv_address = view.tv_address fun bind(listViewItem: ListViewItem) { view.setOnClickListener(View.OnClickListener { Toast.makeText(view.context,"Name :::: "+ listViewItem.name +" /n Address :::"+ listViewItem.address, Toast.LENGTH_LONG).show() }) } } 

puede llamar desde el adaptador en el métodoBindViewHolder … por ejemplo:

  override fun onBindViewHolder(holder: ViewHolder, position: Int) { val listViewItem: ListViewItem = mListViewItems[position] holder.tv_message.text = listViewItem.name holder.tv_address.text = listViewItem.address holder.bind( mListViewItems[position]); } 
  • RecyclerView.ViewHolder no se vincula correctamente
  • RecyclerView animation de elementos, se llama AnimationFinished, pero no está activado en AnimationStarted. ¿Por qué?
  • ¿Cómo agregar un oyente Onclick en un RecyclerView en Android Studio con Kotlin?
  • ¿Cómo uso un onItemLongClickListener en mi actividad en Android?
  • DiffUtils y multi selección
  • ¿Cómo agregar una image más como un button al final de mi RecyclerView en Android?
  • ReciclarVer list de elementos que no aparecen
  • My RecyclerView no se notifica adecuadamente
  • ¿Por qué onclick en los ítems de RecyclerView se devuelve una identificación de artículo incorrecta?
  • extensiones kotlin-android en ViewHolder
  • De la documentation de kotlin no está claro para mí el operador como