与ListView不同,Android RecyclerView似乎过于复杂而无法实现。由于RecyclerView子系统没有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
设置onclicklistener设置它在你的ViewHolder
的Adapter.
类的构造函数内
一些示例代码将类似于以下类,它是recyclerview适配器中的内部类。
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;
}
谢谢大家!我想出了一个解决方案(尽管它对我有用,但我不确定该技术的效率如何)。我在View.OnClickListener
课程中使用RecyclerView.Adapter
界面。为了防止大量垃圾收集,我在onCreateViewHolder
的RecyclerView.Adapter
方法中分配了点击监听器。这是我的RecyclerView.Adapter
类的完整实现:
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
注册点击事件,请告诉我。
虽然已经有了一些答案,但我想我可能会提供我的实现以及解释。 (详情可在another similar question I answered上找到)。
因此,要添加单击侦听器,您的内部ViewHolder
类需要实现View.OnClickListener
。这是因为你将OnClickListener
设置为itemView
构造函数的ViewHolder
参数。让我告诉你我的意思:
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
的工作原理。
看来你已经得到了问题的答案,但这里的答案都没有尝试使用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操作符吗?)。
你为什么不尝试一下?
您应该在RecyclerView中的各个视图上设置单击侦听器,而不是整个RecyclerView。这可以通过在ViewHolder类的构造函数中设置侦听器来实现,类似于以下示例:
class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View view, OnClickListener listener) {
super(view);
view.setOnClickListener(listener);
}
}
晚会,但也许这将有助于其他人。
我发现将OnClickListener
附加到RecyclerView
中项目或儿童视图的最佳位置是在RecyclerView.Adapter
的onCreateViewHolder
中。由于RecyclerView
回收/重用项目ViewHolders
,它是创建/附加监听器的最有效的地方,因为它们仅在创建新的持有者时创建和附加,因此比onBindViewHolder
更频繁甚至绑定(在持有者内部)类)。
但是,只需要将更改注册到特定数据集项(适配器数据阵列/列表中特定位置的项),需要2-3件事。
如果您有多种ViewHolder类型,并且该信息有助于确定单击时发生的情况。那么你需要
ViewHolder
类型或访问Adapter.getViewHolderType()
。您需要的更明显的事情是点击位置的数据集(数组/列表)项目,这样您就可以在该项目中注册特定于项目的更改(以便在项目滚动屏幕时更改保留在项目中) 。这意味着您需要访问
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,并相应地显示时间textview。
请注意,通过在ViewHolder的构造函数中创建OnCLickListener可以实现相同的结果,这是因为在OnCreateViewHolder()中调用了该构造函数。这样做的好处是可以使适配器做得更少并将决策/细节委托给持有者,从而允许使适配器更简单(更少的代码行)并且更通用。否定的是你不能只创建一个OnClickListener来不加区别地附加到所有持有者类型;你必须在每个viewholder类的构造函数中创建监听器。虽然不是一个重大问题。