问题
我正在探索RecyclerView
,我惊讶地发现 RecyclerView
没有 onItemClickListener()
。因为RecyclerView
继承自 android.view.ViewGroup
而 ListView
继承自 android.widget.AbsListView
。但是我通过在RecyclerView.Adapter
中编写onClick
来解决我的问题:
public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
public TextView txtViewTitle;
public ImageView imgViewIcon;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
}
@Override
public void onClick(View v) {
}
}
但我仍然想知道为什么 Google 删除 onItemClickListener()
?是否存在性能问题或其他问题?
#1 热门回答(1128 赞)
tl; dr 2016使用 RxJava 和 PublishSubject 公开点击的 Observable。
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();
}
}
原帖:
自引入ListView 以来,onItemClickListener一直存在问题。当你有任何内部元素的点击监听器的时候,回调将不会被触发,但它没有被通知或没有很好的文档记录(如果有的话),所以有很多混淆和SO问题。
鉴于RecyclerView
更进一步,没有行/列的概念,而是任意布局的子项数量,他们已经将onClick委托给它们中的每一个,或者程序员实现。
把Recyclerview
看作是一个 ListView 替代品,而不是一个更复杂用例的灵活组件。正如你所说,你的解决方案是谷歌期望你做的。现在你有一个适配器可以将 onClick 委派给在构造函数中传递的接口,这是ListView
和Recyclerview
的正确模式。
public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
public TextView txtViewTitle;
public ImageView imgViewIcon;
public IMyViewHolderClicks mListener;
public ViewHolder(View itemLayoutView, IMyViewHolderClicks listener) {
super(itemLayoutView);
mListener = listener;
txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
imgViewIcon.setOnClickListener(this);
itemLayoutView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v instanceof ImageView){
mListener.onTomato((ImageView)v);
} else {
mListener.onPotato(v);
}
}
public static interface IMyViewHolderClicks {
public void onPotato(View caller);
public void onTomato(ImageView callerImage);
}
}
然后在你的适配器上
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
String[] mDataset = { "Data" };
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_layout, parent, false);
MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() {
public void onPotato(View caller) { Log.d("VEGETABLES", "Poh-tah-tos"); };
public void onTomato(ImageView callerImage) { Log.d("VEGETABLES", "To-m8-tohs"); }
});
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// Get element from your dataset at this position
// Replace the contents of the view with that element
// Clear the ones that won't be used
holder.txtViewTitle.setText(mDataset[position]);
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
return mDataset.length;
}
...
现在查看最后一段代码:onCreateViewHolder(ViewGroup parent,int viewType)
签名已经提示了不同的视图类型。对于他们中的每一个,您都需要不同的查看者,随后他们每个人都可以拥有不同的点击次数。或者你可以创建一个通用的视图,它可以获取任何视图和一个onClickListener
并相应地应用。或者将一个级别委派给协调器,以便多个片段/活动具有相同的具有不同点击行为的列表。再一次,所有的灵活性都在你身边。
它是一个非常需要的组件,并且与我们迄今为止对ListView`的内部实现和改进非常接近。谷歌最终承认它是很好的。
#2 热门回答(79 赞)
我喜欢这种方式,我正在使用它
内
public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
放
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_image_and_text, parent, false);
v.setOnClickListener(new MyOnClickListener());
并在你想要的地方创建这个类
class MyOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
int itemPosition = recyclerView.indexOfChild(v);
Log.e("Clicked and Position is ",String.valueOf(itemPosition));
}
}
我之前读过的有更好的方法,但我喜欢这种方式很简单,并不复杂。
#3 热门回答(78 赞)
另一种解决方案是Android GDE的Hugo Visser oneproposed。他为您提供免授权课程,只需放入您的代码并使用它即可。
用法:
ItemClickSupport.addTo(mRecyclerView)
.setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
@Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
(它也支持长时间点击)
实施(由我添加的评论):
public class ItemClickSupport {
private final RecyclerView mRecyclerView;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
// ask the RecyclerView for the viewHolder of this view.
// then use it to get the position for the adapter
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
}
};
private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
return false;
}
};
private RecyclerView.OnChildAttachStateChangeListener mAttachListener
= new RecyclerView.OnChildAttachStateChangeListener() {
@Override
public void onChildViewAttachedToWindow(View view) {
// every time a new child view is attached add click listeners to it
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener);
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener);
}
}
@Override
public void onChildViewDetachedFromWindow(View view) {
}
};
private ItemClickSupport(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
// the ID must be declared in XML, used to avoid
// replacing the ItemClickSupport without removing
// the old one from the RecyclerView
mRecyclerView.setTag(R.id.item_click_support, this);
mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
}
public static ItemClickSupport addTo(RecyclerView view) {
// if there's already an ItemClickSupport attached
// to this RecyclerView do not replace it, use it
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support == null) {
support = new ItemClickSupport(view);
}
return support;
}
public static ItemClickSupport removeFrom(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support != null) {
support.detach(view);
}
return support;
}
public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
return this;
}
public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
mOnItemLongClickListener = listener;
return this;
}
private void detach(RecyclerView view) {
view.removeOnChildAttachStateChangeListener(mAttachListener);
view.setTag(R.id.item_click_support, null);
}
public interface OnItemClickListener {
void onItemClicked(RecyclerView recyclerView, int position, View v);
}
public interface OnItemLongClickListener {
boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
}
}
还要创建一个values / ids.xml
文件并将其放入:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_click_support" type="id" />
</resources>
这个类通过将RecyclerView.OnChildAttachStateChangeListener
附加到RecyclerView
来工作。每次儿童与“RecyclerView”连接或分离时都会通知该监听器。该代码使用这个将点击/长按听众附加到视图。该监听器向RecyclerView
询问包含该位置的RecyclerView.ViewHolder
。
如果您需要更多,您也可以调整代码,让您回到持有人身上。
请记住,通过在列表中的每个视图上设置一个点击监听器,就像在其他答案中一样,在您的适配器中处理它是完全正确的。这不是最有效的做法(每次重新使用视图时都会创建一个新的侦听器),但它可以工作,而且在大多数情况下这不是问题。
关于为什么RecyclerView
没有onItemClickListener
。RecyclerView
是一个工具箱,与之前的ListView
相比,它具有更少的特性和更多的灵活性。 onItemClickListener
不是从ListView中删除的唯一功能。但它有很多听众和方法可以根据自己的喜好进行扩展,它在正确的手中更加强大)。
在我看来,RecyclerView
中删除的最复杂的功能是Fast Scroll。大多数其他功能可以很容易地重新实现。