我们可以在 RecyclerView 行项目上使用内置的 setOnClickListener 而不是自定义项目点击监听器吗?

Can we make use of the built in setOnClickListener on RecyclerView row item instead of custom item click listener?

我见过很多为行项目提供点击功能(用于在回收站视图中选择行项目)的例子,但他们都使用自定义点击监听器 class 来实现这一点。有什么方法可以实现这个点击功能吗?

检测对 RecyclerView 项目的点击非常容易。您需要做的就是定义一个接口(如果您不使用 Kotlin,在这种情况下您只需传入一个 lambda):

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
    private final Clicks clicks;

    public MyAdapter(Clicks clicks) {
        this.clicks = clicks;
    }

    private List<MyObject> items = Collections.emptyList();

    public void updateData(List<MyObject> items) {
        this.items = items;
        notifyDataSetChanged(); // TODO: use ListAdapter for diffing instead if you need animations
    }

    public interface Clicks {
        void onItemSelected(MyObject myObject, int position);
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        private MyObject myObject;    

        public MyViewHolder(View view) {
            super(view);
            // bind views
            view.setOnClickListener((v) -> {
                int adapterPosition = getAdapterPosition();
                if(adapterPosition >= 0) {
                    clicks.onItemSelected(myObject, adapterPosition);
                }
            });
        }

        public void bind(MyObject myObject) {
            this.myObject = myObject;
            // bind data to views
        }
    }
}

Kotlin 中的相同代码:

class MyAdapter(val itemClicks: (MyObject, Int) -> Unit): RecyclerView.Adapter<MyViewHolder>() {
    private var items: List<MyObject> = Collections.emptyList()

    fun updateData(items: List<MyObject>) {
        this.items = items
        notifyDataSetChanged() // TODO: use ListAdapter for diffing instead if you need animations
    }

    inner class MyViewHolder(val myView: View): RecyclerView.ViewHolder(myView) {
        private lateinit var myObject: MyObject

        init {
            // binds views
            myView.onClick {
                val adapterPosition = getAdapterPosition()
                if(adapterPosition >= 0) {
                    itemClicks.invoke(myObject, adapterPosition)
                }
            }
        }

        fun bind(myObject: MyObject) {
            this.myObject = myObject
            // bind data to views
        }
    }
}

如果您使用的是 Kotlin,则可以将方法作为构造函数参数或通过 setter 方法发送。你不需要在 kotlin 中为这种情况创建任何接口监听器。

class ImageCategoryAdapter(val listener: (YourType) -> Unit) : RecyclerView.Adapter<ImageCategoryAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_poll_category, parent, false))
    }

    override fun getItemCount() = yourList.size

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

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        // ...
        init {
            // this is the magic
            itemView.setOnClickListener { listener.invoke(yourList[adapterPosition]) }
        }
    }
}

在适配器中,我们将函数作为名为 listener 的构造函数参数。然后在单击项目视图时,我们将调用具有所需类型的函数。

val adapter = ImageCategoryAdapter {
    // Your callback 
    // here it will be type of parameter which we are sending from adapter by invoking the function.
    processClickedItem(it)
}

这就是我对 Kotlin 上瘾的原因。这种情况不需要接口回调。

已更新 如果你也想发位置。

class ImageCategoryAdapter(val listener: (YourType, Int) -> Unit) : RecyclerView.Adapter<ImageCategoryAdapter.ViewHolder>() {
 ...
}

您必须使用两个参数调用此函数。

itemView.setOnClickListener { listener.invoke(yourList[adapterPosition], adapterPosition) }

现在我们可以看到有多个参数,所以 it 对参数不起作用,我们必须为这个 lambda 函数提供变量名。

val adapter = ImageCategoryAdapter { clickItem, position -> processClickedItem(clickItem, position)}