Kotlin中的RecyclerView itemClickListener

Afz*_*ivE 39 android kotlin android-recyclerview

我在使用Android 3年后,正在Kotlin编写我的第一个应用程序.只是混淆了如何在Kotlin中使用itemClickListener和RecyclerView.

我尝试过特征(编辑:现在界面)方法,非常类似于Java

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)
  }
}
Run Code Online (Sandbox Code Playgroud)

这似乎非常多余,所以我尝试了内部类方法:

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

然后只需设置适配器的点击监听器,如下所示:

val adapter = WorkoutsAdapter(ItemClickListener())
Run Code Online (Sandbox Code Playgroud)

但我仍然对此不满意,因为我认为可能会有更好,更清洁的方式.我试图基本上实现这样的事情: RecyclerView onClick

有什么建议?

结束了批准的答案的变化

定义了活动中的功能:

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

将函数本身传递给适配器,如下所示:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

循环中的扩展功能在下面的批准答案中.

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

den*_*rle 40

我的解决方案就像以前的解决方案一样,来自活动的超级干净通话.

ContactAdapter:

class ContactAdapter @Inject constructor() : RecyclerView.Adapter<ContactAdapter.ViewHolder>() {

    var onItemClick: ((Contact) -> Unit)? = null
    var contacts: List<Contact> = emptyList()

    ...

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val contact = contacts[position]

        holder.email.text = contact.email
    }

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val email: TextView = itemView.email

        init {
            itemView.setOnClickListener {
                onItemClick?.invoke(contacts[adapterPosition])
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ContactActivity:

override fun setupRecyclerAdapter() {
    recyclerView.adapter = contactAdapter
    recyclerView.layoutManager = LinearLayoutManager(this)

    contactAdapter.onItemClick = { contact ->

        // do something with your item
        Log.d("TAG", contact.email)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 哦不错,我喜欢这个 (3认同)
  • 请记住清除 onDestroyView 的引用,因为这可能会泄漏。 (2认同)
  • 这个“覆盖有趣的 setupRecyclerAdapter”是什么?它从哪里覆盖? (2认同)

Dam*_*tla 25

我有一点不同的方法.您可以为ViewHolder创建扩展程序

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

然后在这样的适配器中使用它

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

    }
}
Run Code Online (Sandbox Code Playgroud)

我和我的同事一起在图书馆提供这样的扩展.

  • 这在Adapter中调用.如何在mainActivity中使用它作为回调? (5认同)

Ang*_*oko 15

您可以通过使用接口轻松实现这一点

class ExercisesAdapter(private val itemClickListener: ItemClickListener) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    interface ItemClickListener {
        fun onItemClick(position: Int)
        fun onLongClick(position: Int)
    }

    inner class MyViewHolder(view:View): RecyclerView.ViewHolder(view){

        init {
            view.setOnClickListener {
                if (bindingAdapterPosition >= 0) {
                    itemClickListener.onItemClick(bindingAdapterPosition)
                }
            }

            view.setOnLongClickListener{
                if (bindingAdapterPosition >= 0) { 
                    itemClickListener.onLongClick(bindingAdapterPosition)
                }
                return@setOnLongClickListener true
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

来自您的MainActivity

public class MainActivity : AppCompatActivity(), ExercisesAdapter.ItemClickListener {

   protected override fun onCreate(savedInstanceState: Bundle?) {

       // set content view etc go above this line
       mAdapter = ExercisesAdapter(this)
   }

   override fun onItemClick(position: Int) {
        Toast.makeText(this@MainActivity, "TEST: " + position, Toast.LENGTH_SHORT).show()
    }

    override fun onLongClick(position: Int) {
        //do long click here
    }
}
Run Code Online (Sandbox Code Playgroud)


Raj*_*ddy 11

对不起,很抱歉,链接提供了一个很棒的答案,它是Java的。做了一些功课,并将其转换为Kotlin。

现在它可以正常工作了。这是代码,

创建一个名为RecyclerItemClickListenr的类,

class RecyclerItemClickListenr(context: Context, recyclerView: RecyclerView, private val mListener: OnItemClickListener?) : RecyclerView.OnItemTouchListener {

private val mGestureDetector: GestureDetector

interface OnItemClickListener {
    fun onItemClick(view: View, position: Int)

    fun onItemLongClick(view: View?, position: Int)
}

init {

    mGestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
        override fun onSingleTapUp(e: MotionEvent): Boolean {
            return true
        }

        override fun onLongPress(e: MotionEvent) {
            val childView = recyclerView.findChildViewUnder(e.x, e.y)

            if (childView != null && mListener != null) {
                mListener.onItemLongClick(childView, recyclerView.getChildAdapterPosition(childView))
            }
        }
    })
}

override fun onInterceptTouchEvent(view: RecyclerView, e: MotionEvent): Boolean {
    val childView = view.findChildViewUnder(e.x, e.y)

    if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
        mListener.onItemClick(childView, view.getChildAdapterPosition(childView))
    }

    return false
}

override fun onTouchEvent(view: RecyclerView, motionEvent: MotionEvent) {}

override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}}
Run Code Online (Sandbox Code Playgroud)

并以“活动/片段”身份访问

recyclerView.addOnItemTouchListener(RecyclerItemClickListenr(this, recyclerView, object : RecyclerItemClickListenr.OnItemClickListener {

        override fun onItemClick(view: View, position: Int) {
            //do your work here..
        }
        override fun onItemLongClick(view: View?, position: Int) {
            TODO("do nothing")
        }
    }))
Run Code Online (Sandbox Code Playgroud)

  • 当您可以在项目视图上设置单击侦听器时,为什么需要手势检测器和触摸事件处理? (2认同)

not*_*ato 10

如果有人正在寻找更简单答案,我尝试了以下 - 这与AfzalivE的解决方案非常相似:

在我的Adapter类中,我传递了clickListener一个参数.在onBindViewHolder,我曾经setOnClickListener打电话clickListener和处理点击事件.

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

}
Run Code Online (Sandbox Code Playgroud)

注意:我需要从列表项的容器(根视图)中引用,在本例中是containerView

然后我将我的对象作为参数传递,而不需要再次在列表中搜索它并直接在我的Activity类上处理它,在我设置适配器的那一刻:

MyActivity.kt:

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

更新

如果您需要获取所单击项目的位置,只需将其定义为回调中的参数,然后稍后将其发回.请注意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...
Run Code Online (Sandbox Code Playgroud)

然后在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) }
}
Run Code Online (Sandbox Code Playgroud)

MyActivity.kt上,您必须更改设置适配器的方式,以便获得该位置.像这样:

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

  • @YLS 刚刚用更新编辑了帖子。检查上面:) (2认同)
  • 我喜欢这个简单易懂的 (2认同)
  • 我喜欢它!非常简洁的答案。 (2认同)

iOS*_*fee 8

在 onBindViewHolder 上添加 ClickListener 代码乐趣

override fun onBindViewHolder(holder: ViewHolder, position: Int) {

    holder.vieww.textView.setText(arr.get(position))

    holder.vieww.setOnClickListener {(holder.vieww.textView.setTextColor(Color.GREEN))} // click event
}
Run Code Online (Sandbox Code Playgroud)

  • 最简单的答案,但最好的! (3认同)

小智 7

您不需要向 ViewHolder 或类似的东西编写扩展函数。
最佳实践; 使用高阶函数

主回收器适配器

class MainRecyclerAdapter(val news: JSONArray, private val itemClickListener: (Int) -> Unit) : RecyclerView.Adapter<MainRecyclerAdapter.ViewHolder>() {}
Run Code Online (Sandbox Code Playgroud)

只需添加一个高阶函数即可。像itemClickListener然后转到ViewHolder类。将此函数作为参数写入您的绑定函数,并将其设置为 itemView 像这样:

MainRecyclerAdapter.ViewHolder

 class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {

        fun bind(newsItem: JSONObject,itemClickListener:(Int)->Unit) {
            //Some Stuff here..

            itemView.setOnClickListener { itemClickListener(adapterPosition) }

        }
    }
Run Code Online (Sandbox Code Playgroud)

在BindViewHolder上使用这个方法

绑定视图持有者

 override fun onBindViewHolder(holder: MainRecyclerAdapter.ViewHolder, position: Int) {

        holder.bind(news.getJSONObject(position),itemClickListener)
    }
Run Code Online (Sandbox Code Playgroud)

现在您可以在任何活动或片段中编写 onClick 函数。只需提供参数即可。

活动或片段

val itemOnClick: (Int) -> Unit = { position ->
                newsRecyclerView.adapter!!.notifyDataSetChanged()
                Toast.makeText(this.context,"$position. item clicked.",Toast.LENGTH_SHORT).show()
            }
 newsRecyclerView.adapter = MainRecyclerAdapter(news,itemClickListener = itemOnClick)
Run Code Online (Sandbox Code Playgroud)


小智 7

基于denwehrle略有不同

要在片段上使用,请在OnCreateView内

 adapter.onItemClick = {it ->
    //do something
 }
Run Code Online (Sandbox Code Playgroud)

添加适配器类:

var onItemClick: ((Contact)->Unit) ?= null
...

inner class contactViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
        val myItemView: TextView = itemView.findViewById(R.id.textView)

        init{
            itemView.setOnClickListener {
                onItemClick?.invoke(contact[adapterPosition])
            }
        }
}
Run Code Online (Sandbox Code Playgroud)


Max*_*llu 5

于05-2019更新

我认为最优雅的解决方案是将责任赋予recyclerView,而不是查看甚至改编它。

为此,我们需要:

1:创建RecyclerItemClickListener文件

class RecyclerItemClickListener(
        private val mRecycler: RecyclerView,
        private val clickListener: ((position: Int, view: View) -> Unit)? = null,
        private val longClickListener: ((position: Int, view: View) -> Unit)? = null
) : RecyclerView.OnChildAttachStateChangeListener {

    override fun onChildViewDetachedFromWindow(view: View) {
        view.setOnClickListener(null)
        view.setOnLongClickListener(null)
    }

    override fun onChildViewAttachedToWindow(view: View) {
        view.setOnClickListener { v -> setOnChildAttachedToWindow(v) }
    }

    private fun setOnChildAttachedToWindow(v: View?) {
        if (v != null) {
            val position = mRecycler.getChildLayoutPosition(v)
            if (position >= 0) {
                clickListener?.invoke(position, v)
                longClickListener?.invoke(position, v)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

2:为RecyclerView创建/添加扩展名:

import android.support.v7.widget.RecyclerView
import com.app.manager.internal.common.RecyclerItemClickListener

@JvmOverloads
fun RecyclerView.affectOnItemClicks(onClick: ((position: Int, view: View) -> Unit)? = null, onLongClick: ((position: Int, view: View) -> Unit)? = null) {
    this.addOnChildAttachStateChangeListener(RecyclerItemClickListener(this, onClick, onLongClick))
}
Run Code Online (Sandbox Code Playgroud)

3:最后使用(我想您使用kotlinx)

import kotlinx.android.synthetic.main.{your_layout_name}.*

class FragmentName : Fragment() {

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        recycler.affectOnItemClick { position, view -> /*todo*/ }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个解决方案,但您的直觉是正确的 - 这里的样板太多了。虽然需要几遍才能理解 kotlin 语法,但可以在这里找到一个不错的方法:https://antonioleiva.com/recyclerview-adapter-kotlin/ (2认同)

Kis*_*nki 5

3个简单的步骤:

1. 传入你的适配器参数如下:

class ListAdapter(private val mListener: (ListItemDataClass) -> Unit)
Run Code Online (Sandbox Code Playgroud)

2.在onBindViewHolder功能上,像这样使用

override fun onBindViewHolder(holder: YourViewHolder, position: Int) {

    val item = getItem(position)

    holder.itemView.setOnClickListener {
            item?.let { it1 -> mListener.invoke(it1) }
    }
}
Run Code Online (Sandbox Code Playgroud)

3.在你的活动中,像这样使用

val adapter = ListAdapter {
        Toast.makeText(this, it.title, Toast.LENGTH_SHORT).show()
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这是一种不使用接口的简单方法,只需在适配器中在 viewholder 类中创建一个 init 块即可。像下面这样

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    init {
        itemView.setOnClickListener{
            //your code here---
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我希望人们停止回答这个问题。它不需要 24 个答案。尤其是看到这个问题有多古老了,早在 Kotlin 中 Trait 还很重要的时候。 (3认同)