RecyclerView 的 OnClickListener

OnClickListener for RecyclerView

与 ListView 不同,Android RecyclerView 似乎实现起来太复杂了。由于 RecyclerView child 没有 OnItemClickListener,我一直在尝试实现以下内容来注册点击事件:

final RecyclerView rv=(RecyclerView)findViewById(R.id.recycler_view);
    LinearLayoutManager llm=new LinearLayoutManager(this);
    rv.setLayoutManager(llm);
    MyRVAdapter rva=new MyRVAdapter(persons);
    rv.setAdapter(rva);

    rv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int itemPosition = rv.indexOfChild(v);
            Log.d("Tag", String.valueOf(itemPosition));
        }
    });

出于某种原因,我无法使此代码正常工作。点击事件根本没有注册!谁能告诉我我做错了什么?我已经看到了一些解决方案,但我认为这应该有效。感谢您的帮助!

您应该在 RecyclerView 中的各个视图上设置点击侦听器,而不是将 RecyclerView 作为一个整体。这可以通过在 ViewHolder class 的构造函数中设置侦听器来实现,类似于下面的示例:

class ViewHolder extends RecyclerView.ViewHolder {
    public ViewHolder(View view, OnClickListener listener) {
        super(view);             
        view.setOnClickListener(listener);
    }
}

不要为整个 RecyclerView 设置 onclicklistener,而是将其设置在 Adapter.

ViewHolder class 的构造函数中

一些示例代码将类似于下面的 class,它是 recyclerview 适配器内部的一个内部 class。

     class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

      .....
        public ViewHolder(View itemView) {
            super(itemView);
           .......
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {

           .......
        }
    }

在适配器的 onCreateViewHolder 内部,将膨胀视图传递给 ViewGroup 的构造函数,例如

 @Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View v = LayoutInflater.from(viewGroup.getContext())
            .inflate(R.layout.your_layout, viewGroup, false);
    ViewHolder viewHolder = new ViewHolder(v);
    return viewHolder;
}    

谢谢大家!我想出了一个解决方案(虽然它对我有用,但我不确定该技术的效率如何)。我在 RecyclerView.Adapter class 中使用 View.OnClickListener 界面。为了防止大量垃圾收集,我在 RecyclerView.AdapteronCreateViewHolder 方法中分配了点击监听器。这是我的 RecyclerView.Adapter class:

的完整实现
package com.example.evinish.recyclerdemo;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

/**
 * Created by evinish on 9/25/2015.
 */
public class MyRVAdapter extends RecyclerView.Adapter<MyViewHolder> implements View.OnClickListener{

    List<Person> persons;

    public MyRVAdapter(List<Person> persons) {
        this.persons = persons;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout,parent,false);
        MyViewHolder vh=new MyViewHolder(view);
        view.setOnClickListener(this);

        return vh;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.company.setText(persons.get(position).getCompany());
        holder.occupation.setText(persons.get(position).getOccupation());
        holder.name.setText(persons.get(position).getName());


    }

    @Override
    public int getItemCount() {
        return persons.size();
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }

    public void clearAdapter() {
        persons.clear();
        notifyDataSetChanged();
    }

    @Override
    public void onClick(View v) {
        TextView clickedName=(TextView)v.findViewById(R.id.card_text1);
        Toast.makeText(v.getContext(),clickedName.getText().toString(),Toast.LENGTH_LONG).show();

    }
}

如果RecyclerView中有更好的注册点击事件的方法请告诉我。

虽然已经有一些答案,但我想我可以提供我的实现以及解释。 (可以在 上找到完整的详细信息)。

因此,要添加点击侦听器,您的内部 ViewHolder class 需要实现 View.OnClickListener。这是因为您将 OnClickListener 设置为 ViewHolder 构造函数的 itemView 参数。让我告诉你我的意思:

public class ExampleClickViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    TextView text1, text2;

    ExampleClickViewHolder(View itemView) {
        super(itemView);

        // we do this because we want to check when an item has been clicked:
        itemView.setOnClickListener(this);

        // now, like before, we assign our View variables
        title = (TextView) itemView.findViewById(R.id.text1);
        subtitle = (TextView) itemView.findViewById(R.id.text2);
    }

    @Override
    public void onClick(View v) {
        // The user may not set a click listener for list items, in which case our listener
        // will be null, so we need to check for this
        if (mOnEntryClickListener != null) {
            mOnEntryClickListener.onEntryClick(v, getLayoutPosition());
        }
    }
}

您唯一需要添加的其他内容是 Adapter 的自定义界面和 setter 方法:

private OnEntryClickListener mOnEntryClickListener;

public interface OnEntryClickListener {
    void onEntryClick(View view, int position);
}

public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
    mOnEntryClickListener = onEntryClickListener;
}

那么您新的支持点击的 Adapter 就完成了。

现在,让我们使用它...

    ExampleClickAdapter clickAdapter = new ExampleClickAdapter(yourObjects);
    clickAdapter.setOnEntryClickListener(new ExampleClickAdapter.OnEntryClickListener() {
        @Override
        public void onEntryClick(View view, int position) {
            // stuff that will happen when a list item is clicked
        }
    });

这基本上就是您设置普通 Adapter 的方式,只是您使用自己创建的 setter 方法来控制当用户单击特定列表项时您将执行的操作。

我在 this Gist on GitHub 上制作了一组示例,其中显示了完整的 Java 文件,您可以将其用作模板,或帮助您了解 Adapter 的工作原理。

您的问题似乎已经有了答案,但此处 none 的答案试图使用 RxJava.

解决此问题

我本人是 RxJava 的忠实粉丝,只要有可能我就不会错过使用它的机会。

这是我用的,

public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    String[] mDataset = { "Data", "In", "Adapter" };

    private final PublishSubject<String> onClickSubject = PublishSubject.create();

    @Override 
    public void onBindViewHolder(final ViewHolder holder, int position) {
        final String element = mDataset[position];

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               onClickSubject.onNext(element);
            }
        });
    }

    public Observable<String> getPositionClicks(){
        return onClickSubject.asObservable();
    }
}

它公开了一个Observable来拦截点击事件。您现在可以完全控制您想要对点击事件执行的操作(还记得 RxJava 运算符吗?)。

你为什么不试一试?

聚会迟到了,但也许这会对其他人有所帮助。

我发现将 OnClickListener 附加到 RecyclerView 中的项目或项目的子视图的最佳位置是 RecyclerView.AdapteronCreateViewHolder。由于项目 ViewHoldersRecyclerView 的 recycling/reusing,它是 create/attach 听众最有效的地方,因为它们仅在创建新持有人时创建和附加,并且因此比 onBindViewHolder 中的频率低得多,甚至绑定(在持有人 class 内)。

但是,仅需要 2-3 件事情来注册对特定数据集项目的更改(适配器数据中特定位置的项目 array/list)。

如果您有多个 ViewHolder 类型,并且该信息有助于确定单击时发生的情况。那么你需要

  1. ViewHolder 类型或访问 Adapter.getViewHolderType()

您需要的更明显的事情是被点击位置的数据集 (array/list) 项目,这样您就可以在该项目中注册特定于项目的更改(以便更改保留在项目中项目滚动到屏幕外)。这意味着您需要访问

  1. RecyclerView
  2. 的数据集(通常是数组或列表)
  3. 需要更改的特定项目在数据集中的位置

您可能还需要调用 notifyItemChanged()notifyItemInserted() 或(效率较低的 notifyDataSetChanged()

例如,在我的一个项目的适配器中:

// Inflates the appropriate layout according to the ViewType.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view;
    if (viewType == HOLDER_VIEW_TYPE_ONE) {          
        view = LayoutInflater.from(mContext).inflate(R.layout.type_one_holder_layout, parent, false);
        final RecyclerView.ViewHolder holder = new TypeOneHolder(mContext,view);

        //Place onclick listener after holder. holder needs to be final
        //but thats fine as onCreateViewHolder usually just returns holder
        //right after its created. onBindViewHolder is where changes are
        //made.
        view.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View view) {
                final int position = holder.getAdapterPosition();
                if (position != RecyclerView.NO_POSITION) {
                    if (mDataList.get(position).getTimeDisplayState() == ON_STATE) {
                        mMessageList.get(position).setTimeDisplayState(DEFAULT_STATE);
                    } else {
                        mMessageList.get(position).setTimeDisplayState(ON_STATE);
                    }
                    notifyItemChanged(position);
                }
            }

        });
        return holder;
    } 
}

请注意,当单击 recyclerview 行时,我使用它来显示和隐藏 recyclerview 项目中的最后更新时间。在我的 ViewHolder.bind() 中检查了 timeDisplayState,并相应地显示了时间文本视图。

请注意,通过在 ViewHolder 的构造函数中创建 OnCLickListener 可以获得相同的结果,这是因为在 OnCreateViewHolder() 中调用了该构造函数。这样做的好处是可以让适配器做更少的事情并将 decisions/details 委托给持有者,从而使适配器更简单(更少的代码行)和更通用。不利的是,您不能只创建一个 OnClickListener 来不加区别地附加到所有持有者类型;你必须在每个 viewholder class 的构造函数中创建监听器。不过,这并不是什么大问题。