Слушатель щелчка элемента RecyclerView правильным способом
Я использую RecyclerView
адаптер для отображения данных внутри действия, я хочу реализовать 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, чтобы у меня было больше контроля. Это не соответствует моей цели. Я думаю, что это будет полезно для многих из нас.
Переведено автоматически
Ответ 1
Вам нужно ознакомиться с этим руководством здесь, чтобы лучше понять, как вы можете добиться желаемого поведения.
В случае обработки 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
методе установите click listener.
@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);
}
});
}
}
}
Ответ 2
Регистрация clickListener
внутри onCreateViewHolder
вместо onBindViewHolder
является более производительной, поскольку вы добавляете прослушиватель только при создании представления, а не при прокрутке RecyclerView.
И я использую ListAdapter с обратным вызовом DiffUtil вместо 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
with payLoad
, которое позволяет обновлять только некоторую часть строк, а не всю строку целиком. Например, у вас есть изображение, заголовок и основная часть в строках, и часто меняется только основная часть, изображение полезной нагрузки не мигает и обеспечивает плохое взаимодействие с пользователем. Но с помощью полезной нагрузки вы можете решить, какая часть строки должна быть обновлена, что позволяет вам не перезагружать части, которые не были обновлены.
Ответ 3
Я нашел супер-пупер простой метод! Я рекомендую этот
Пример кода:
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();
}
}));
я нашел это из здесь
Надеюсь, это помогло вам.
Ответ 4
По-моему, я только что создал единственный экземпляр ClickListener
, И он отправляет событие click как для 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")
}