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.Adapter
的 onCreateViewHolder
方法中分配了点击监听器。这是我的 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.Adapter
的 onCreateViewHolder
。由于项目 ViewHolders
的 RecyclerView
的 recycling/reusing,它是 create/attach 听众最有效的地方,因为它们仅在创建新持有人时创建和附加,并且因此比 onBindViewHolder
中的频率低得多,甚至绑定(在持有人 class 内)。
但是,仅需要 2-3 件事情来注册对特定数据集项目的更改(适配器数据中特定位置的项目 array/list)。
如果您有多个 ViewHolder 类型,并且该信息有助于确定单击时发生的情况。那么你需要
ViewHolder
类型或访问 Adapter.getViewHolderType()
。
您需要的更明显的事情是被点击位置的数据集 (array/list) 项目,这样您就可以在该项目中注册特定于项目的更改(以便更改保留在项目中项目滚动到屏幕外)。这意味着您需要访问
RecyclerView
和 的数据集(通常是数组或列表)
- 需要更改的特定项目在数据集中的位置
您可能还需要调用 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 的构造函数中创建监听器。不过,这并不是什么大问题。
与 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.Adapter
的 onCreateViewHolder
方法中分配了点击监听器。这是我的 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.Adapter
的 onCreateViewHolder
。由于项目 ViewHolders
的 RecyclerView
的 recycling/reusing,它是 create/attach 听众最有效的地方,因为它们仅在创建新持有人时创建和附加,并且因此比 onBindViewHolder
中的频率低得多,甚至绑定(在持有人 class 内)。
但是,仅需要 2-3 件事情来注册对特定数据集项目的更改(适配器数据中特定位置的项目 array/list)。
如果您有多个 ViewHolder 类型,并且该信息有助于确定单击时发生的情况。那么你需要
ViewHolder
类型或访问Adapter.getViewHolderType()
。
您需要的更明显的事情是被点击位置的数据集 (array/list) 项目,这样您就可以在该项目中注册特定于项目的更改(以便更改保留在项目中项目滚动到屏幕外)。这意味着您需要访问
RecyclerView
和 的数据集(通常是数组或列表)
- 需要更改的特定项目在数据集中的位置
您可能还需要调用 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 的构造函数中创建监听器。不过,这并不是什么大问题。