Как добавить обработчик нажатия на элемент в RecyclerView?

IP696

Мне нужен горизонтальный лист вью. Стал рыть в интернете, и нашел совет юзать RecyclerView. Вроде это новая штука от гугл и тд. Вобщем вставил я его себе в проект. Выводит он все нормально. Написал адаптер и тд. Но как теперь написать обработчик нажатия на айтем??? нашел реализацию этого всего вот тут введите сюда описание ссылки

Но в итоге получаю такое

mRecyclerView.addOnItemTouchListener( new RecyclerItemClickListener(this, new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(DashboardActivity.this, "dfsdg"+ position, Toast.LENGTH_LONG ).show(); } }) );

как мне вместо "dfsdg"+ position вывести элемент моего списка??? гугл в очередной раз меня огорчил.

ДОПОЛНИЛ

я добавил метод в адаптер.

public User getItem(int position){ return userList.get(position); }

теперь я имею возможность из вне вытащить свой айтем

@Override public void onItemClick(View view, int position) { Toast.makeText(DashboardActivity.this, ((RecyclerAdapter)mAdapter).getItem(position).getUserInfo().getName(), Toast.LENGTH_LONG ).show(); }

Но мне не нравиться то, что я вынужден передавать mAdapter. В старом обычном листе можно было обратиться к адаптеру из листвью. Тут этого нет, и как это сделать я не знаю.

Как-то так

public void onItemClick(AdapterView<!--?--> adapterView, View view, int i, long l) { adapterView.getAdapter().getItem(i)...... }

adapterView, а не передавать свой адаптер.

2 ответа

IP696

Для начала, почему же google не реализовала интерфейс onItemClickListener в своем новом виджете RecyclerView. Объясняется это тем, что данный интерфейс был весьма не совершенен и, в частности, создавал определенные трудности для обработки кликов на вложенных элементах айтема, также были определенные проблемы с получением реальной позиции и некоторые другие. Чтобы избавить себя от подобных проблем google решила доверить реализацию этого сомнительного действия самому программисту ... что же, какие у нас есть варианты.

В первую очередь нужно понять, какого экшена мы чаще всего ожидаем от клика по элементу списка? Как правило это переход к другой активити со значением кликнутой позиции (каких то связанных с этой позицией данных адаптера и тп).

Реализовать такое простое действие можно просто повешав слушатель прямо в адаптере. Здесь у нас есть доступ к текущей позиции и всем данным адаптера:

@Override public void onBindViewHolder( ItemHolder holder, int position) { holder.someView.setOnClickListener (new View.OnClickListener() { @Override public void onClick(View v) { // action on click } }); }

Однако IP696 в своем комментарии был прав, вешать сложную логику (если такая вдруг потребовалась для обработки клика по айтему) в адаптер не разумно.

Следующий вариант - положить на доводы google и реализовать интерфейс onItemClicklListener() самостоятельно. При такой реализации мы можем передать "на сторону" через интерфейс множество полезных вещей: позицию, данные адаптера по кликнутому айтему, view айтема и тд. , но лишаем себя удобной возможности простым способом обрабатывать клики на дочерних элементах, как это можно было сделать в первом варианте и о чем предупреждал нас google. Кроме того, фактически, как правило, нет никакой прямой необходимости обрабатывать клик по айтему на "внешней" стороне. Пример:

public class ListAdapterHolder extends RecyclerView.Adapter<listadapterholder.viewholder> { OnItemClickListener mItemClickListener; @Override public ViewHolder onCreateViewHolder(ViewGroup parent , int viewType) { ... return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder holder , int position) { ... } public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public ViewHolder(View view) { super(view); view.setOnClickListener(this); ... //здесь можно повесить слушатель и на отдельные виджеты в айтеме } @Override public void onClick(View v) { if (mItemClickListener != null) { mItemClickListener.onItemClick(v, getAdapterPosition()); } } } public interface OnItemClickListener { public void onItemClick(View view , int position); } public void SetOnItemClickListener(final OnItemClickListener mItemClickListener) { this.mItemClickListener = mItemClickListener; }
}
</listadapterholder.viewholder>

Наиболее логичным решением ситуации я считаю решение обрабатывать клики в классе-холдере адаптера. Здесь у нас есть простой и прямой доступ ко всем элементам айтема, данные самого адаптера (так как класс вложен в адаптер) для простого добавления\изменения\удаления пунктов списка, кроме того через конструктор можно передать дополнительные данные, кроме, собственно View, также логика обработки кликов выведена из адаптера в статический холдер:

public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener { TextView mText; Button mButton; CardView mCard; public ItemHolder(View v) { super(v); mCard = (CardView) v.findViewById(R.id.item_card); mText = (TextView) v.findViewById(R.id.item_text); mButton = (Button) v.findViewById(R.id.item_button); mCard.setOnClickListener(this); mButton.setOnClickListener(this); } @Override public void onClick(View v) { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { switch (v.getId()) { case R.id.card: itemClick(position); break; case R.id.button: buttonClick(position); } } } private void itemClick(int position){ //action on item click } private void buttonClick (int position){ //action on button click } }

Так же, если клик нужно обработать в активити, то можно просто указать в xml-разметке айтема списка метод обработки клика (android:onClick) и реализовать его в активити. Подробнее


IP696

Ответ @pavlofff верен. Единственный вопрос где обработать нажатие. В своем проекте мы используем для этого шину событий (Otto by Square). Что то вроде:

public class Adapter extends RecyclerView.Adapter<recyclerview.viewholder> { @Inject Bus bus; @Override public void onBindViewHolder( ItemHolder holder, final int position) { holder.view.setOnClickListener (new View.OnClickListener() { @Override public void onClick(View v) { bus.post(new ItemClickedEvent(getItem(position))); } }); }
}
</recyclerview.viewholder>

В нужном месте ловим ItemClickedEvent, который содержит в себе модель, ячейка с которой была выбрана. Странно, конечно, что Google не добавила callback как для listview.

licensed under cc by-sa 3.0 with attribution.