我正在探索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) {
}
}
但我还是想知道为什么谷歌删除了onItemClickListener()
?
是否有性能问题或其他问题?
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
1:1 替代品,而不是复杂用例的更灵活组件。正如你所说,你的解决方案是谷歌对你的期望。现在你有了一个适配器,可以将 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
内部实现和改进非常接近。谷歌最终承认这一点很好。
另一种解决方案是由 Android GDE 的Hugo Visser 提出的解决方案。他为您提供了免费许可课程,您只需输入代码并使用它即可。
用法:
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 。大多数其他功能可以轻松重新实现。
我喜欢这种方式而且我正在使用它
内
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));
}
}
我以前读过有更好的方法,但我喜欢这种方式很容易而且不复杂。