RecyclerView itemClickListener в Котлине

Я пишу свое первое приложение в Kotlin после 3-летнего опыта работы с Android. Просто запутался, как использовать itemClickListener с RecyclerView в Котлине.

Я пробовал подход к признаку,

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) } } 

Это казалось очень избыточным, поэтому я попытался использовать внутренний класс:

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

А затем просто установите прослушиватель клипов адаптера следующим образом:

 val adapter = WorkoutsAdapter(ItemClickListener()) 

Но я все еще не удовлетворен этим, потому что думаю, что может быть лучший, более чистый способ. Я пытаюсь добиться чего-то вроде этого: RecyclerView onClick

Какие-либо предложения?

Закончился с изменением утвержденного ответа

Определена функция в действии:

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

Передал его на адаптер следующим образом:

 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) } } 

Функция расширения с помощью Loop в одобренном ответе ниже.

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

У меня есть немного другой подход. Вы можете создать расширение для своего ViewHolder

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

Затем используйте его в адаптере, как это

 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) { } } 

Я работаю с коллегами по библиотеке, предоставляя такие расширения.

В случае, если кто-то ищет более беззаботный ответ, я попробовал следующее – что очень похоже на решение от AfzalivE :

В классе Adapter я передал параметр clickListener в качестве параметра. В onBindViewHolder я использовал setOnClickListener для вызова clickListener и обработки события 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)... } 

Примечание . Мне нужна ссылка из контейнера элемента списка (корневого представления), который в этом случае является containerView

Затем я передал свой объект как параметр без необходимости повторного поиска его в списке и обработал его непосредственно в моем классе Activity , в тот момент, когда я установил адаптер:

MyActivity.kt :

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

Обновить

Если вам нужно получить позицию щелкнутого элемента, просто определите его как параметр в обратном вызове, а затем отправьте его позже. Обратите внимание на val clickListener: (MyObject, Int) -> Unit ниже:

MyAdapter.kt

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

Затем на onBindViewHolder() вы передаете позицию при вызове метода обратного вызова:

 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) } } 

И на MyActivity.kt , вам придется изменить способ установки адаптера, чтобы вы могли получить позицию. Как это:

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

Вы можете попробовать что-то вроде:

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

и преобразование SAM просто работает, как в Java 8, поэтому просто используйте лямбда:

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

В RecyclerView вы можете поместить щелчок на завышенном представлении внутри класса viewholder и вызвать его из метода обратного вызова onBindViewHolder, например:

  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() }) } } 

вы можете позвонить из адаптера onBindViewHolder … например:

  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]); }