RecyclerView Item Click Listener 的正确方式
RecyclerView Item Click Listener the Right Way
我使用 RecyclerView
适配器在 activity 中显示数据,我想在 activity 中实现 onClickListener
,目前,我正在设置 onClickListener
像往常一样在适配器内部工作正常。
public void onBindViewHolder(MyHolder holder, final int position) {
final Listdata data = listdata.get(position);
holder.vname.setText(data.getName());
holder.vname.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}
});
}
但是我想在 activity 中实现它,这样我就有更大的控制权。这不符合我的目的。我认为这对我们很多人都有用。
您可以让您的 Activity
实现 View.OnClickListener
并将其传递给适配器。下面是一个例子。
class RAdapter extends RecyclerView.Adapter<>{
View.OnClickListener listner;
public RAdapter(View.OnClickListener listner) {
this.listner = listner;
}
public void onBindViewHolder(MyHolder holder, final int position) {
holder.vname.setOnClickListener(listner);
}
}
但是要处理 Activity
中的点击,您将需要点击位置。您可以使用 adapter.getAdapterPosition()
来验证单击了哪个项目。
除此之外,要将点击事件传递给 Fragment/Activity,您可以使用自定义回调侦听器,这样您的 Adapter
将可重复使用。
在 ViewHolder
中处理点击的更好方法。请参阅以下示例。
class Holder extends RecyclerView.ViewHolder implements View.OnClickListener {
Button button;
public Holder(View itemView) {
super(itemView);
button=itemView.findViewById(R.id.b1);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if(v.getId()==R.id.b1){
int position=getAdapterPosition();
// Call the call method here
// with position or data Object itself
}
}
}
如果我没理解错的话,您想在 Activity 中设置点击逻辑。
您可以通过在 Activity 中设置 OnClickListener 并将其传递给 Adapter 构造函数来实现。
MyAdapter myAdapter = new MyAdapter(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}
}));
而您的 MyAdapter 构造函数将是:
final private OnClickListener onClickListener;
public MyAdapter(OnClickListener onClickListener) {
this.OnClickListener = OnClickListener;
}
所以你的新代码应该是这样的
public void onBindViewHolder(MyHolder holder, final int position) {
final Listdata data = listdata.get(position);
holder.vname.setText(data.getName());
holder.vname.setOnClickListener(onClickListener);
}
您需要在此处查看 this tutorial 以便更好地了解如何实现您想要的行为。
在处理来自 activity 的 onClickListener
的情况下,您需要基于带有接口的回调实现来工作。将接口从 activity 传递到您的适配器,然后在单击某些项目时从您的适配器调用回调函数。
这是教程中的示例实现。
让我们先来看看界面。
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
您需要修改您的适配器以将侦听器作为参数,如下所述。
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
现在在您的 onBindViewHolder
方法中,设置点击侦听器。
@Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
...
itemView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
现在在您的 RecyclerView
中设置适配器。
recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
@Override public void onItemClick(ContentItem item) {
Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
}
}));
所以整个适配器代码如下所示。
public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
return new ViewHolder(v);
}
@Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
@Override public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private ImageView image;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
image = (ImageView) itemView.findViewById(R.id.image);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
name.setText(item.name);
Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
itemView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
}
}
Create an interface for the adapter class
private OnItemClickListener mListener;
public CustomAdapter(List<Listdata> listdata, OnItemClickListener listener) {
mListener = listener;
...
...
}
private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ViewHolder(View view) {
...
...
view.setOnClickLister(this);
}
@override
public void onClick(View v) {
mListener.onAdapterItemClick(getAdapterPosition())
}
}
interface OnItemClickListener {
void onAdapterItemClick(int position);
}
Let the activity implement the interface
public class CustomListActivity extends AppCompatActivity implements OnItemClickListener {
...
...
@override
public void onAdapterItemClick(int position) {
Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}
还有另一种方法,查看this实现
我找到了超级简单的方法!我推荐这个
示例代码:
public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
return new ViewHolder(v);
}
@Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
@Override public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private ImageView image;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
image = (ImageView) itemView.findViewById(R.id.image);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
name.setText(item.name);
Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
itemView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
}
}
并使用以下代码使用 RecyclerView 适配器:
recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
@Override public void onItemClick(ContentItem item) {
Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
}
}));
我从
中找到了这个
希望对你有所帮助。
非常简单干净的解决方案是:
create a class with the name of RecyclerTouchListener:
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildPosition(child));
}
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public interface ClickListener {
void onClick(View view, int position);
void onLongClick(View view, int position);
}
}
在您的回收站视图中 activity:
recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() {
@Override
public void onClick(View view, int position) {
speech(countries_list_code[position]);
}
@Override
public void onLongClick(View view, int position) {
}
}));
我的项目中总是有一个通用适配器,以避免每次使用 Recyclerview 时都创建一个适配器 class。这里有一些例子
public class AdapterRecyclerviewTextOnly extends RecyclerView.Adapter<AdapterRecyclerviewTextOnly.ViewHolder> {
private RecyclerView recyclerView;
private OnRecyclerviewListener onRecyclerviewListener;
public interface OnRecyclerviewListener {
void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position);
void onRecyclerviewClick(RecyclerView recyclerView, int position);
int onItemCount(RecyclerView recyclerView);
}
public void setOnRecyclerviewListener(OnRecyclerviewListener listener) { this.onRecyclerviewListener = listener; }
public AdapterRecyclerviewTextOnly(RecyclerView recyclerView) {
super();
this.recyclerView = recyclerView;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
RecyclerView recyclerView;
public TextView textView;
ViewHolder(RecyclerView recyclerView, View itemView) {
super(itemView);
this.recyclerView = recyclerView;
this.itemView.setOnClickListener(this);
this.textView = itemView.findViewById(R.id.textview_title);
}
void onBind(int position) { onRecyclerviewListener.onRecyclerviewBind(this.recyclerView, this, position); }
@Override
public void onClick(View v) {
onRecyclerviewListener.onRecyclerviewClick(this.recyclerView, getAdapterPosition());
}
}
@Override
public AdapterRecyclerviewTextOnly.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View inflatedView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview_text_only, parent, false);
return new ViewHolder(this.recyclerView, inflatedView);
}
@Override
public void onBindViewHolder(AdapterRecyclerviewTextOnly.ViewHolder holder, int position) {
holder.onBind(position);
}
@Override
public int getItemCount() {
return onRecyclerviewListener.onItemCount(this.recyclerView);
}
}
然后在您的 Activity Class 中,您可以将此适配器用于:
this.recyclerView = findViewById(R.id.recyclerview);
this.recyclerView.setHasFixedSize(true);
this.recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
AdapterRecyclerviewTextOnly recyclerViewAdapter = new AdapterRecyclerviewTextOnly(this.recyclerView);
this.recyclerView.setAdapter(this.recyclerViewAdapter);
this.recyclerViewAdapter.setOnRecyclerviewListener(new AdapterRecyclerviewTextOnly.OnRecyclerviewListener() {
@Override
public void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position) {
}
@Override
public void onRecyclerviewClick(RecyclerView recyclerView, int position) {
}
@Override
public int onItemCount(RecyclerView recyclerView) {
}
});
您也可以在 2 或 3 个 recyclerview 中重复使用它。
首先,声明一个 globar 监听器 private AdapterRecyclerviewTextOnly.OnRecyclerviewListener listener;
.
然后用新对象初始化监听器,然后用监听器设置你的每个回收视图。使用特定标识符:
if (recyclerView == recyclerViewA){ } else if (recyclerView == recyclerViewB) { }
在适配器内管理您的回收视图。
以我的方式,我只是创建了一个 ClickListener
实例,并将点击事件分派给 RecyclerView
和 Activity 或 Fragment:
class LeagueAdapter(
onLeagueSelected: (League, Int, View) -> Unit
) : RecyclerView.Adapter<LeagueHolder>() {
private val dataSet = arrayListOf<League>()
private val clickListener = View.OnClickListener { view ->
val adapterPosition = view.tag as Int
onLeagueSelected(dataSet[adapterPosition], adapterPosition, view)
// perform adapter related action here ...
}
override fun getItemCount(): Int {
return dataSet.size
}
override fun onBindViewHolder(holder: LeagueHolder, position: Int) {
// put item position in tag field
holder.itemView.tag = position
holder.itemView.setOnClickListener(clickListener)
}
}
在 Activity 里面,我们有这样的东西:
private val headerAdapter = LeagueAdapter { league, i, view ->
Log.e(TAG, "item clicked $i")
}
就个人而言,我喜欢通过 RxJava 主题来处理这个问题:
Subject 是一种桥梁或代理,在 ReactiveX 的某些实现中可用,它既充当观察者又充当 Observable。因为它是一个观察者,所以它可以订阅一个或多个Observable,并且因为它是一个Observable,所以它可以通过re-emitting它们观察到的item,它也可以发出新的item。
有关详细信息,请阅读 Understanding RxJava Subject — Publish, Replay, Behavior and Async Subject。
在适配器中:
public static PublishSubject<MyData> onClickSubject = PublishSubject.create();
ViewHolder:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
.
.
.
@Override
public void onClick(View view) {
onClickSubject.onNext(getItem(getAdapterPosition()));
}
}
将您的一次性用品添加到 CompositeDisposable 并在 onDestroy() 中处理它们:
private CompositeDisposable compositeDisposable = new CompositeDisposable();
在 onCreate() 中:
compositeDisposable.add(MyAdapter.onClickSubject.subscribe(myData -> {
//do something here
}));
在 onDestroy() 中:
compositeDisposable.dispose();
注:
1. getItem() 是 androidx.recyclerview.widget.ListAdapter 和 androidx.paging.PagedListAdapter 的一种方法,如果你扩展 RecyclerView.Adapter 你可以从你的按位置排列的数据列表。
2. 要使用 Disposables 你需要 RxJava2 或更高版本
在 onCreateViewHolder
而不是 onBindViewHolder
内注册 clickListener
性能更高,因为您只在创建视图时添加侦听器,而不是在滚动 recyclerView 时添加侦听器。
我使用带有 DiffUtil 回调的 ListAdapter 而不是 RecyclerViewAdapter
abstract class BaseListAdapter<ItemType>(
callBack: DiffUtil.ItemCallback<ItemType> = DefaultItemDiffCallback(),
private inline val onItemClicked: ((ItemType, Int) -> Unit)? = null
) : ListAdapter<ItemType, BaseItemViewHolder>(
AsyncDifferConfig.Builder<ItemType>(callBack)
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
.build()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseItemViewHolder {
return BaseItemViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
getLayoutRes(viewType),
parent, false
)
).apply {
onViewHolderCreated(this, viewType, binding)
}
}
fun createCustomViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return BaseItemViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
getLayoutRes(viewType),
parent, false
)
)
}
override fun onBindViewHolder(
holder: BaseItemViewHolder,
position: Int,
payloads: MutableList<Any>
) {
val item: ItemType? = currentList.getOrNull(position)
item?.let {
holder.binding.setVariable(BR.item, item)
onViewHolderBound(holder.binding, item, position, payloads)
holder.binding.executePendingBindings()
}
}
override fun onBindViewHolder(holder: BaseItemViewHolder, position: Int) {
}
/**
* get layout res based on view type
*/
protected abstract fun getLayoutRes(viewType: Int): Int
/**
* Called when a ViewHolder is created. ViewHolder is either created first time or
* when data is refreshed.
*
* This method is not called when RecyclerView is being scrolled
*/
open fun onViewHolderCreated(
viewHolder: RecyclerView.ViewHolder,
viewType: Int,
binding: ViewDataBinding
) {
binding.root.setOnClickListener {
onItemClicked?.invoke(getItem(viewHolder.bindingAdapterPosition), viewHolder.bindingAdapterPosition)
}
}
/**
* bind view while RecyclerView is being scrolled and new items are bound
*/
open fun onViewHolderBound(
binding: ViewDataBinding,
item: ItemType,
position: Int,
payloads: MutableList<Any>
) {
}
}
open class BaseItemViewHolder(
val binding: ViewDataBinding
) : RecyclerView.ViewHolder(binding.root)
class DefaultItemDiffCallback<ItemType> : DiffUtil.ItemCallback<ItemType>() {
override fun areItemsTheSame(
oldItem: ItemType,
newItem: ItemType
): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(
oldItem: ItemType,
newItem: ItemType
): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
}
另一个更好的用户体验是将 onBindViewHolder
与 payLoad
结合使用,这样您就可以只更新部分行而不是整行。例如,您在行中有图像、标题和 body,只有 body 经常更改,没有负载图像闪烁并提供糟糕的用户体验。但是使用有效载荷,您可以决定应更新行的哪一部分,从而避免重新加载未更新的部分。
RecyclerView
小部件只有 2 个对这种情况有用 listeners:
- RecyclerView.OnChildAttachStateChangeListener - covered here
- RecyclerView.OnItemTouchListener - 我将要介绍的那个
该代码的灵感来自与 Accessibility 相关的 TouchEvents 示例,并在 Activity
/Fragment
中运行,无需在 Adapter
中设置任何侦听器
recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
var downTouch = false
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_DOWN -> downTouch = true
MotionEvent.ACTION_UP -> if (downTouch) {
downTouch = false
recyclerView.findChildViewUnder(e.x, e.y)?.let {
val position = rv.getChildAdapterPosition(it)
Toast.makeText(rv.context, "clicked on $position", Toast.LENGTH_SHORT)
.show()
}
}
else -> downTouch = false
}
return super.onInterceptTouchEvent(rv, e)
}
})
CodePath中记录了另一种非常简单的方法。
ItemClickSupport.addTo(recyclerView).setOnItemClickListener(
new ItemClickSupport.OnItemClickListener() {
@Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do stuff
}
}
);
我使用 RecyclerView
适配器在 activity 中显示数据,我想在 activity 中实现 onClickListener
,目前,我正在设置 onClickListener
像往常一样在适配器内部工作正常。
public void onBindViewHolder(MyHolder holder, final int position) {
final Listdata data = listdata.get(position);
holder.vname.setText(data.getName());
holder.vname.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}
});
}
但是我想在 activity 中实现它,这样我就有更大的控制权。这不符合我的目的。我认为这对我们很多人都有用。
您可以让您的 Activity
实现 View.OnClickListener
并将其传递给适配器。下面是一个例子。
class RAdapter extends RecyclerView.Adapter<>{
View.OnClickListener listner;
public RAdapter(View.OnClickListener listner) {
this.listner = listner;
}
public void onBindViewHolder(MyHolder holder, final int position) {
holder.vname.setOnClickListener(listner);
}
}
但是要处理 Activity
中的点击,您将需要点击位置。您可以使用 adapter.getAdapterPosition()
来验证单击了哪个项目。
除此之外,要将点击事件传递给 Fragment/Activity,您可以使用自定义回调侦听器,这样您的 Adapter
将可重复使用。
在 ViewHolder
中处理点击的更好方法。请参阅以下示例。
class Holder extends RecyclerView.ViewHolder implements View.OnClickListener {
Button button;
public Holder(View itemView) {
super(itemView);
button=itemView.findViewById(R.id.b1);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if(v.getId()==R.id.b1){
int position=getAdapterPosition();
// Call the call method here
// with position or data Object itself
}
}
}
如果我没理解错的话,您想在 Activity 中设置点击逻辑。
您可以通过在 Activity 中设置 OnClickListener 并将其传递给 Adapter 构造函数来实现。
MyAdapter myAdapter = new MyAdapter(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}
}));
而您的 MyAdapter 构造函数将是:
final private OnClickListener onClickListener;
public MyAdapter(OnClickListener onClickListener) {
this.OnClickListener = OnClickListener;
}
所以你的新代码应该是这样的
public void onBindViewHolder(MyHolder holder, final int position) {
final Listdata data = listdata.get(position);
holder.vname.setText(data.getName());
holder.vname.setOnClickListener(onClickListener);
}
您需要在此处查看 this tutorial 以便更好地了解如何实现您想要的行为。
在处理来自 activity 的 onClickListener
的情况下,您需要基于带有接口的回调实现来工作。将接口从 activity 传递到您的适配器,然后在单击某些项目时从您的适配器调用回调函数。
这是教程中的示例实现。
让我们先来看看界面。
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
您需要修改您的适配器以将侦听器作为参数,如下所述。
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
现在在您的 onBindViewHolder
方法中,设置点击侦听器。
@Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
...
itemView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
现在在您的 RecyclerView
中设置适配器。
recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
@Override public void onItemClick(ContentItem item) {
Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
}
}));
所以整个适配器代码如下所示。
public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
return new ViewHolder(v);
}
@Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
@Override public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private ImageView image;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
image = (ImageView) itemView.findViewById(R.id.image);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
name.setText(item.name);
Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
itemView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
}
}
Create an interface for the adapter class
private OnItemClickListener mListener;
public CustomAdapter(List<Listdata> listdata, OnItemClickListener listener) {
mListener = listener;
...
...
}
private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ViewHolder(View view) {
...
...
view.setOnClickLister(this);
}
@override
public void onClick(View v) {
mListener.onAdapterItemClick(getAdapterPosition())
}
}
interface OnItemClickListener {
void onAdapterItemClick(int position);
}
Let the activity implement the interface
public class CustomListActivity extends AppCompatActivity implements OnItemClickListener {
...
...
@override
public void onAdapterItemClick(int position) {
Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}
还有另一种方法,查看this实现
我找到了超级简单的方法!我推荐这个
示例代码:
public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
public interface OnItemClickListener {
void onItemClick(ContentItem item);
}
private final List<ContentItem> items;
private final OnItemClickListener listener;
public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
this.items = items;
this.listener = listener;
}
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
return new ViewHolder(v);
}
@Override public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(items.get(position), listener);
}
@Override public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private ImageView image;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
image = (ImageView) itemView.findViewById(R.id.image);
}
public void bind(final ContentItem item, final OnItemClickListener listener) {
name.setText(item.name);
Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
itemView.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
listener.onItemClick(item);
}
});
}
}
}
并使用以下代码使用 RecyclerView 适配器:
recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
@Override public void onItemClick(ContentItem item) {
Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
}
}));
我从
希望对你有所帮助。
非常简单干净的解决方案是:
create a class with the name of RecyclerTouchListener:
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildPosition(child));
}
}
});
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public interface ClickListener {
void onClick(View view, int position);
void onLongClick(View view, int position);
}
}
在您的回收站视图中 activity:
recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() {
@Override
public void onClick(View view, int position) {
speech(countries_list_code[position]);
}
@Override
public void onLongClick(View view, int position) {
}
}));
我的项目中总是有一个通用适配器,以避免每次使用 Recyclerview 时都创建一个适配器 class。这里有一些例子
public class AdapterRecyclerviewTextOnly extends RecyclerView.Adapter<AdapterRecyclerviewTextOnly.ViewHolder> {
private RecyclerView recyclerView;
private OnRecyclerviewListener onRecyclerviewListener;
public interface OnRecyclerviewListener {
void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position);
void onRecyclerviewClick(RecyclerView recyclerView, int position);
int onItemCount(RecyclerView recyclerView);
}
public void setOnRecyclerviewListener(OnRecyclerviewListener listener) { this.onRecyclerviewListener = listener; }
public AdapterRecyclerviewTextOnly(RecyclerView recyclerView) {
super();
this.recyclerView = recyclerView;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
RecyclerView recyclerView;
public TextView textView;
ViewHolder(RecyclerView recyclerView, View itemView) {
super(itemView);
this.recyclerView = recyclerView;
this.itemView.setOnClickListener(this);
this.textView = itemView.findViewById(R.id.textview_title);
}
void onBind(int position) { onRecyclerviewListener.onRecyclerviewBind(this.recyclerView, this, position); }
@Override
public void onClick(View v) {
onRecyclerviewListener.onRecyclerviewClick(this.recyclerView, getAdapterPosition());
}
}
@Override
public AdapterRecyclerviewTextOnly.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View inflatedView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview_text_only, parent, false);
return new ViewHolder(this.recyclerView, inflatedView);
}
@Override
public void onBindViewHolder(AdapterRecyclerviewTextOnly.ViewHolder holder, int position) {
holder.onBind(position);
}
@Override
public int getItemCount() {
return onRecyclerviewListener.onItemCount(this.recyclerView);
}
}
然后在您的 Activity Class 中,您可以将此适配器用于:
this.recyclerView = findViewById(R.id.recyclerview);
this.recyclerView.setHasFixedSize(true);
this.recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
AdapterRecyclerviewTextOnly recyclerViewAdapter = new AdapterRecyclerviewTextOnly(this.recyclerView);
this.recyclerView.setAdapter(this.recyclerViewAdapter);
this.recyclerViewAdapter.setOnRecyclerviewListener(new AdapterRecyclerviewTextOnly.OnRecyclerviewListener() {
@Override
public void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position) {
}
@Override
public void onRecyclerviewClick(RecyclerView recyclerView, int position) {
}
@Override
public int onItemCount(RecyclerView recyclerView) {
}
});
您也可以在 2 或 3 个 recyclerview 中重复使用它。
首先,声明一个 globar 监听器 private AdapterRecyclerviewTextOnly.OnRecyclerviewListener listener;
.
然后用新对象初始化监听器,然后用监听器设置你的每个回收视图。使用特定标识符:
if (recyclerView == recyclerViewA){ } else if (recyclerView == recyclerViewB) { }
在适配器内管理您的回收视图。
以我的方式,我只是创建了一个 ClickListener
实例,并将点击事件分派给 RecyclerView
和 Activity 或 Fragment:
class LeagueAdapter(
onLeagueSelected: (League, Int, View) -> Unit
) : RecyclerView.Adapter<LeagueHolder>() {
private val dataSet = arrayListOf<League>()
private val clickListener = View.OnClickListener { view ->
val adapterPosition = view.tag as Int
onLeagueSelected(dataSet[adapterPosition], adapterPosition, view)
// perform adapter related action here ...
}
override fun getItemCount(): Int {
return dataSet.size
}
override fun onBindViewHolder(holder: LeagueHolder, position: Int) {
// put item position in tag field
holder.itemView.tag = position
holder.itemView.setOnClickListener(clickListener)
}
}
在 Activity 里面,我们有这样的东西:
private val headerAdapter = LeagueAdapter { league, i, view ->
Log.e(TAG, "item clicked $i")
}
就个人而言,我喜欢通过 RxJava 主题来处理这个问题:
Subject 是一种桥梁或代理,在 ReactiveX 的某些实现中可用,它既充当观察者又充当 Observable。因为它是一个观察者,所以它可以订阅一个或多个Observable,并且因为它是一个Observable,所以它可以通过re-emitting它们观察到的item,它也可以发出新的item。
有关详细信息,请阅读 Understanding RxJava Subject — Publish, Replay, Behavior and Async Subject。
在适配器中:
public static PublishSubject<MyData> onClickSubject = PublishSubject.create();
ViewHolder:
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
.
.
.
@Override
public void onClick(View view) {
onClickSubject.onNext(getItem(getAdapterPosition()));
}
}
将您的一次性用品添加到 CompositeDisposable 并在 onDestroy() 中处理它们:
private CompositeDisposable compositeDisposable = new CompositeDisposable();
在 onCreate() 中:
compositeDisposable.add(MyAdapter.onClickSubject.subscribe(myData -> {
//do something here
}));
在 onDestroy() 中:
compositeDisposable.dispose();
注:
1. getItem() 是 androidx.recyclerview.widget.ListAdapter 和 androidx.paging.PagedListAdapter 的一种方法,如果你扩展 RecyclerView.Adapter 你可以从你的按位置排列的数据列表。
2. 要使用 Disposables 你需要 RxJava2 或更高版本
在 onCreateViewHolder
而不是 onBindViewHolder
内注册 clickListener
性能更高,因为您只在创建视图时添加侦听器,而不是在滚动 recyclerView 时添加侦听器。
我使用带有 DiffUtil 回调的 ListAdapter 而不是 RecyclerViewAdapter
abstract class BaseListAdapter<ItemType>(
callBack: DiffUtil.ItemCallback<ItemType> = DefaultItemDiffCallback(),
private inline val onItemClicked: ((ItemType, Int) -> Unit)? = null
) : ListAdapter<ItemType, BaseItemViewHolder>(
AsyncDifferConfig.Builder<ItemType>(callBack)
.setBackgroundThreadExecutor(Executors.newSingleThreadExecutor())
.build()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseItemViewHolder {
return BaseItemViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
getLayoutRes(viewType),
parent, false
)
).apply {
onViewHolderCreated(this, viewType, binding)
}
}
fun createCustomViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return BaseItemViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
getLayoutRes(viewType),
parent, false
)
)
}
override fun onBindViewHolder(
holder: BaseItemViewHolder,
position: Int,
payloads: MutableList<Any>
) {
val item: ItemType? = currentList.getOrNull(position)
item?.let {
holder.binding.setVariable(BR.item, item)
onViewHolderBound(holder.binding, item, position, payloads)
holder.binding.executePendingBindings()
}
}
override fun onBindViewHolder(holder: BaseItemViewHolder, position: Int) {
}
/**
* get layout res based on view type
*/
protected abstract fun getLayoutRes(viewType: Int): Int
/**
* Called when a ViewHolder is created. ViewHolder is either created first time or
* when data is refreshed.
*
* This method is not called when RecyclerView is being scrolled
*/
open fun onViewHolderCreated(
viewHolder: RecyclerView.ViewHolder,
viewType: Int,
binding: ViewDataBinding
) {
binding.root.setOnClickListener {
onItemClicked?.invoke(getItem(viewHolder.bindingAdapterPosition), viewHolder.bindingAdapterPosition)
}
}
/**
* bind view while RecyclerView is being scrolled and new items are bound
*/
open fun onViewHolderBound(
binding: ViewDataBinding,
item: ItemType,
position: Int,
payloads: MutableList<Any>
) {
}
}
open class BaseItemViewHolder(
val binding: ViewDataBinding
) : RecyclerView.ViewHolder(binding.root)
class DefaultItemDiffCallback<ItemType> : DiffUtil.ItemCallback<ItemType>() {
override fun areItemsTheSame(
oldItem: ItemType,
newItem: ItemType
): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(
oldItem: ItemType,
newItem: ItemType
): Boolean {
return oldItem.hashCode() == newItem.hashCode()
}
}
另一个更好的用户体验是将 onBindViewHolder
与 payLoad
结合使用,这样您就可以只更新部分行而不是整行。例如,您在行中有图像、标题和 body,只有 body 经常更改,没有负载图像闪烁并提供糟糕的用户体验。但是使用有效载荷,您可以决定应更新行的哪一部分,从而避免重新加载未更新的部分。
RecyclerView
小部件只有 2 个对这种情况有用 listeners:
- RecyclerView.OnChildAttachStateChangeListener - covered here
- RecyclerView.OnItemTouchListener - 我将要介绍的那个
该代码的灵感来自与 Accessibility 相关的 TouchEvents 示例,并在 Activity
/Fragment
中运行,无需在 Adapter
recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
var downTouch = false
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_DOWN -> downTouch = true
MotionEvent.ACTION_UP -> if (downTouch) {
downTouch = false
recyclerView.findChildViewUnder(e.x, e.y)?.let {
val position = rv.getChildAdapterPosition(it)
Toast.makeText(rv.context, "clicked on $position", Toast.LENGTH_SHORT)
.show()
}
}
else -> downTouch = false
}
return super.onInterceptTouchEvent(rv, e)
}
})
CodePath中记录了另一种非常简单的方法。
ItemClickSupport.addTo(recyclerView).setOnItemClickListener(
new ItemClickSupport.OnItemClickListener() {
@Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do stuff
}
}
);