Почему в RecyclerView нет onItemClickListener ()?

1011

Я исследовал RecyclerViewи был удивлен, увидев, что RecyclerViewэтого не произошло onItemClickListener().

У меня два вопроса.

Главный вопрос

Я хочу знать, почему удалили гугл onItemClickListener()?

Есть проблема с производительностью или что-то еще?

Дополнительный вопрос

Я решил свою проблему, написав onClickв моем RecyclerView.Adapter:

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) {

    }
}

Это нормально / есть ли способ лучше?

9
  • 31 год
    Чтобы ваш код работал, вам нужно добавить itemLayoutView.setOnClickListener (this); в конструкторе ViewHolder. 25 янв.
  • 3
    вы задали хороший вопрос ... у многих разработчиков такие же сомнения по поводу RecyclerView и ListView.
    venkat
    16 авг.
  • 21 год
    Вопрос: почему в RecyclerView нет OnItemClickListner ()? Все ответы ниже, кроме 2: Как добавить onclick для RecyclerView
    Manohar
    11 янв.
  • 3
    Желаю, чтобы кто-нибудь на самом деле нашел время, чтобы ответить на QUESTION 22 сен '17 в 20:13
  • 1
    Думаю, они удалили его из-за проблем с производительностью и мусорной памятью. Но посмотрите на принятый ответ ... он вызовет те же проблемы. Я думаю, вы сделали ошибку, приняв это, и теперь другие люди делают ошибки из-за вас :).
    Alex
    26 апр '18 в 8: 292018-04-26 11:29
1231
+50

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было проблематичным. В тот момент, когда у вас есть прослушиватель кликов для любого из внутренних элементов, обратный вызов не будет запущен, но он не был уведомлен или хорошо задокументирован (если вообще был), поэтому было много путаницы и вопросов по этому поводу.

Учитывая, что RecyclerViewэто делает шаг вперед и не имеет концепции строки / столбца, а скорее произвольно размещенного количества дочерних элементов, они делегировали onClick каждому из них или реализации программиста.

Думайте Recyclerviewне как о ListViewзамене 1: 1, а как о более гибком компоненте для сложных случаев использования. И, как вы говорите, ваше решение - это то, чего Google ожидал от вас. Теперь у вас есть адаптер, который может делегировать 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были до сих пор наши внутренние реализации и улучшения . Хорошо, что Google наконец это признает.

60
  • 79
    RecyclerView.ViewHolder имеет метод getPosition (). С некоторыми изменениями в этом коде вы можете передать позицию слушателю. 5 ноя '14 в 20:50
  • 13
    Он полностью пропустил то onBindViewHolder(holder, position), где должна быть заполнена информация. 5 ноя '14 в 21:19
  • 26 год
    @se_bastiaan getPosition()устарел "Этот метод не рекомендуется, поскольку его значение неоднозначно из-за асинхронной * обработки обновлений адаптера. Используйте {@link #getLayoutPosition ()} или * {@link #getAdapterPosition ()} в зависимости от вашего варианта использования. "
    upenpat
    18 марта '15 в 12:26
  • 8
    У вас есть несколько вариантов. Помещение позиции в поле int внутри держателя просмотра, обновление во время привязки, а затем возврат при щелчке - это единица. 11 мая '15 в 12:58
  • 8
    @ MLProgrammer-CiM "У вас есть несколько вариантов. Помещение позиции в поле типа int внутри держателя просмотра, обновление его при привязке, а затем возврат по щелчку - это один." ViewHolder уже имеет это значение и уже установлен. Просто позвониviewHolder.getPosition(); 23 июля '15 в 22: 552015-07-23 19:55
121

Почему у RecyclerView нет onItemClickListener

Это RecyclerViewнабор инструментов, в отличие от старого, в ListViewнем меньше встроенных функций и больше гибкости. Это onItemClickListenerне единственная функция, удаляемая из ListView. Но у него много слушателей и есть способ расширить его по своему вкусу, он намного мощнее в правильных руках;).

На мой взгляд, самая сложная из удаленных функций RecyclerView- это быстрая прокрутка . Большинство других функций можно легко повторно реализовать.

Если вы хотите узнать, какие еще интересные функции RecyclerViewдобавлены, прочтите этот ответ на другой вопрос.

Эффективное использование памяти - решение для onItemClickListener

Это решение было предложено от Hugo Visser , в Android GDE, сразу после того, RecyclerViewбыл освобожден. Он сделал доступным безлицензионный класс, чтобы вы могли просто вставить свой код и использовать его.

Он демонстрирует некоторую универсальность, появившуюся RecyclerViewблагодаря использованию RecyclerView.OnChildAttachStateChangeListener.

Изменить 2019 : версия kotlin мной, java one, от Hugo Visser, хранится ниже

Котлин / Java

Создайте файл values/ids.xmlи поместите в него следующее:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="item_click_support" type="id" />
</resources>

затем добавьте приведенный ниже код в свой источник

Котлин

Использование:

recyclerView.onItemClick { recyclerView, position, v ->
    // do it
}

(он также поддерживает долгий щелчок по элементу и еще одну функцию, которую я добавил, см. ниже).

реализация (моя адаптация к Java-коду Hugo Visser):

typealias OnRecyclerViewItemClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Unit
typealias OnRecyclerViewItemLongClickListener = (recyclerView: RecyclerView, position: Int, v: View) -> Boolean

class ItemClickSupport private constructor(private val recyclerView: RecyclerView) {

    private var onItemClickListener: OnRecyclerViewItemClickListener? = null
    private var onItemLongClickListener: OnRecyclerViewItemLongClickListener? = null

    private val attachListener: RecyclerView.OnChildAttachStateChangeListener = object : RecyclerView.OnChildAttachStateChangeListener {
        override fun onChildViewAttachedToWindow(view: View) {
            // every time a new child view is attached add click listeners to it
            val holder = [email protected](view)
                    .takeIf { it is ItemClickSupportViewHolder } as? ItemClickSupportViewHolder

            if (onItemClickListener != null && holder?.isClickable != false) {
                view.setOnClickListener(onClickListener)
            }
            if (onItemLongClickListener != null && holder?.isLongClickable != false) {
                view.setOnLongClickListener(onLongClickListener)
            }
        }

        override fun onChildViewDetachedFromWindow(view: View) {

        }
    }

    init {
        // the ID must be declared in XML, used to avoid
        // replacing the ItemClickSupport without removing
        // the old one from the RecyclerView
        this.recyclerView.setTag(R.id.item_click_support, this)
        this.recyclerView.addOnChildAttachStateChangeListener(attachListener)
    }

    companion object {
        fun addTo(view: RecyclerView): ItemClickSupport {
            // if there's already an ItemClickSupport attached
            // to this RecyclerView do not replace it, use it
            var support: ItemClickSupport? = view.getTag(R.id.item_click_support) as? ItemClickSupport
            if (support == null) {
                support = ItemClickSupport(view)
            }
            return support
        }

        fun removeFrom(view: RecyclerView): ItemClickSupport? {
            val support = view.getTag(R.id.item_click_support) as? ItemClickSupport
            support?.detach(view)
            return support
        }
    }

    private val onClickListener = View.OnClickListener { v ->
        val listener = onItemClickListener ?: [email protected]
        // ask the RecyclerView for the viewHolder of this view.
        // then use it to get the position for the adapter
        val holder = this.recyclerView.getChildViewHolder(v)
        listener.invoke(this.recyclerView, holder.adapterPosition, v)
    }

    private val onLongClickListener = View.OnLongClickListener { v ->
        val listener = onItemLongClickListener ?: [email protected] false
        val holder = this.recyclerView.getChildViewHolder(v)
        [email protected] listener.invoke(this.recyclerView, holder.adapterPosition, v)
    }

    private fun detach(view: RecyclerView) {
        view.removeOnChildAttachStateChangeListener(attachListener)
        view.setTag(R.id.item_click_support, null)
    }

    fun onItemClick(listener: OnRecyclerViewItemClickListener?): ItemClickSupport {
        onItemClickListener = listener
        return this
    }

    fun onItemLongClick(listener: OnRecyclerViewItemLongClickListener?): ItemClickSupport {
        onItemLongClickListener = listener
        return this
    }

}

/** Give click-ability and long-click-ability control to the ViewHolder */
interface ItemClickSupportViewHolder {
    val isClickable: Boolean get() = true
    val isLongClickable: Boolean get() = true
}

// Extension function
fun RecyclerView.addItemClickSupport(configuration: ItemClickSupport.() -> Unit = {}) = ItemClickSupport.addTo(this).apply(configuration)

fun RecyclerView.removeItemClickSupport() = ItemClickSupport.removeFrom(this)

fun RecyclerView.onItemClick(onClick: OnRecyclerViewItemClickListener) {
    addItemClickSupport { onItemClick(onClick) }
}
fun RecyclerView.onItemLongClick(onLongClick: OnRecyclerViewItemLongClickListener) {
    addItemClickSupport { onItemLongClick(onLongClick) }
}

(Помните, что вам также необходимо добавить файл XML, см. Выше в этом разделе)

Бонусная функция версии Kotlin

Иногда вам не нужно, чтобы все элементы RecyclerView были интерактивными.

Чтобы справиться с этим, я представил ItemClickSupportViewHolderинтерфейс, который вы можете использовать на своем, ViewHolderчтобы контролировать, какой элемент является интерактивным.

Пример:

class MyViewHolder(view): RecyclerView.ViewHolder(view), ItemClickSupportViewHolder {
    override val isClickable: Boolean get() = false
    override val isLongClickable: Boolean get() = false
}

Джава

Использование:

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);
    }
}

Как это работает (почему эффективно)

Этот класс работает путем присоединения RecyclerView.OnChildAttachStateChangeListenerк RecyclerView. Этот слушатель уведомляется каждый раз, когда дочерний элемент присоединяется или отключается от RecyclerView. Код использует это для добавления прослушивателя касания / длительного щелчка к представлению. Это слушатель спросить RecyclerViewза RecyclerView.ViewHolderкоторый содержит позицию.

Это более эффективно, чем другие решения, поскольку позволяет избежать создания нескольких слушателей для каждого представления и продолжать уничтожать и создавать их во RecyclerViewвремя прокрутки.

Вы также можете адаптировать код, чтобы вернуть сам держатель, если вам нужно больше.

Заключительное замечание

Имейте в виду, что это ПОЛНОСТЬЮ нормально обрабатывать в вашем адаптере, установив для каждого представления вашего списка прослушиватель кликов, как и другой предложенный ответ.

Это просто не самая эффективная вещь (вы создаете нового слушателя каждый раз, когда повторно используете представление), но она работает и в большинстве случаев не является проблемой.

Это также немного против разделения задач, потому что делегировать события щелчка - не совсем задача адаптера.

8
  • addOnChildAttachStateChangeListener в конструкторе и никогда не удалял его?
    Saba
    11 фев '20 в 18:18
  • @Saba вы не должны использовать конструктор напрямую, а через статические методы addTo()и removeFrom(). Экземпляр связан с тегом recyclerview и умирает вместе с ним или при удалении вручную. 19 мар.
  • Так много кода, когда все, что вам нужно сделать, это установить прослушиватель по щелчку, onCreateViewHolderи все. 26 апр '20 в 13:20
  • Ваш аргумент течет, дело не в количестве кода, а в принципе эффективности и ответственности, а количество кода не является отличным показателем хорошего / плохого решения, особенно когда этот код является многоразовым и эффективным. После того, как вы поместите этот код в свой проект (который вы можете рассматривать как включение библиотеки), все, что вам нужно, - это однострочный файл на вашем RecyclerView. 27 апр '20 в 9:25
  • 1
    @DanieleSegato Большое вам спасибо за это! 7 мая '20 в 18:49
95

Мне нравится этот способ и я его использую

Внутри

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));
    }
}

Я читал раньше, что есть способ получше, но мне нравится, что этот способ простой и не сложный.

10
  • 24
    вероятно, следует превратить MyOnClickListenerв переменную экземпляра, потому что вы будете каждый раз создавать ее новый экземпляр с помощью ключевого newслова 25 апр.
  • 14
    recyclerView.getChildPosition (v); лишен возможности использовать recyclerView. indexOfChild (v); 30 мая '15 в 23:26
  • 4
    У вас нет проблем с прокруткой? В моих тестах itemPosition, похоже, зависит от повторного использования представлений, поэтому он идет не так, как мой полный список в адаптере.
    Simon
    01 марта '16 в 17:46
  • 16
    Как попасть recyclerViewв MyOnClickListener?
    guo
    01 окт.
  • 4
    Вместо использования: int itemPosition = recyclerView.indexOfChild (v); вы должны использовать: RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder (v); int itemPosition = holder.getAdapterPosition (); 20 фев '18 в 16:24
41

Android Recyclerview With onItemClickListener, почему мы не можем попробовать это работает, как ListViewтолько.

Источник: Ссылка

public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {

private OnItemClickListener mListener;
public interface OnItemClickListener {
    public void onItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
    mListener = listener;
    mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });
}
@Override
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
    View childView = view.findChildViewUnder(e.getX(), e.getY());
    if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
        mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
    }
    return false;
}

@Override
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
}

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

  }
}

И установите это в RecyclerView:

    recyclerView = (RecyclerView)rootView. findViewById(R.id.recyclerView);
    RecyclerView.LayoutManager mLayoutManager = new            LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addOnItemTouchListener(
            new RecyclerItemClickListener(getActivity(), new   RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                    // TODO Handle item click
                    Log.e("@@@@@",""+position);
                }
            })
    );
3
  • 7
    Честно говоря, не элегантно. 05 июля '16 в 21: 162016-07-05 18:16
  • @vishaldharankar, почему бы и нет? Я думаю, что это лучший способ избежать конфликта смены фокуса после использованияSwipeRefreshLayout 11 июн.
  • В этом нет необходимости, вы можете использовать View.OnClickListener вместо перехвата касания. 26 апр '20 в 13:22
25

Благодаря @marmor я обновил свой ответ.

Я думаю, что это хорошее решение - обработать onClick () в конструкторе класса ViewHolder и передать его родительскому классу через интерфейс OnItemClickListener .

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{

private LayoutInflater layoutInflater;
private List<MyObject> items;
private AdapterView.OnItemClickListener onItemClickListener;

public MyAdapter(Context context, AdapterView.OnItemClickListener onItemClickListener, List<MyObject> items) {
    layoutInflater = LayoutInflater.from(context);
    this.items = items;
    this.onItemClickListener = onItemClickListener;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = layoutInflater.inflate(R.layout.my_row_layout, parent, false);
    return new ViewHolder(view);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    MyObject item = items.get(position);
}

public MyObject getItem(int position) {
    return items.get(position);
}


class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    private TextView title;
    private ImageView avatar;

    public ViewHolder(View itemView) {
        super(itemView);
        title = itemView.findViewById(R.id.title);
        avatar = itemView.findViewById(R.id.avatar);

        title.setOnClickListener(this);
        avatar.setOnClickListener(this);
        itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        //passing the clicked position to the parent class
        onItemClickListener.onItemClick(null, view, getAdapterPosition(), view.getId());
    }
}
}

Использование адаптера в других классах:

MyFragment.java

public class MyFragment extends Fragment implements AdapterView.OnItemClickListener {

private RecyclerView recycleview;
private MyAdapter adapter;

    .
    .
    .

private void init(Context context) {
    //passing this fragment as OnItemClickListener to the adapter
    adapter = new MyAdapter(context, this, items);
    recycleview.setAdapter(adapter);
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    //you can get the clicked item from the adapter using its position
    MyObject item = adapter.getItem(position);

    //you can also find out which view was clicked
    switch (view.getId()) {
        case R.id.title:
            //title view was clicked
            break;
        case R.id.avatar:
            //avatar view was clicked
            break;
        default:
            //the whole row was clicked
    }
}

}
3
  • 34
    Это создаст и соберет множество объектов при прокрутке, вместо этого вы должны создать один OnClickListener и повторно использовать его.
    marmor
    13 мая '15 в 11:27
  • 2
    Также onBindViewHolder()вызывается много раз для одного и того же вида при прокрутке. Даже создание сингла OnClickListenerне поможет, потому что вы будете сбрасывать OnClickListenerкаждый раз, когда onBindViewHolder()вызывается. См. Вместо этого решение @ MLProgrammer-CiM. 25 мая '15 и 20:32
  • Вы также можете улучшить решение @ MLProgrammer-CiM, и вместо создания объекта прослушивателя для каждого представления в этой строке MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ....вы можете создать одного прослушивателя для всех представлений. 25 мая '15 и 20:40
22

Ребята используют этот код в своей основной деятельности. Очень эффективный метод

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.users_list);            
UsersAdapter adapter = new UsersAdapter(users, this);
recyclerView.setAdapter(adapter);
adapter.setOnCardClickListner(this);

Вот ваш класс адаптера.

public class UsersAdapter extends RecyclerView.Adapter<UsersAdapter.UserViewHolder> {
        private ArrayList<User> mDataSet;
        OnCardClickListner onCardClickListner;


        public UsersAdapter(ArrayList<User> mDataSet) {
            this.mDataSet = mDataSet;
        }

        @Override
        public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_row_layout, parent, false);
            UserViewHolder userViewHolder = new UserViewHolder(v);
            return userViewHolder;
        }

        @Override
        public void onBindViewHolder(UserViewHolder holder, final int position) {
            holder.name_entry.setText(mDataSet.get(position).getUser_name());
            holder.cardView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onCardClickListner.OnCardClicked(v, position);
                }
            });
        }

        @Override
        public int getItemCount() {
            return mDataSet.size();
        }

        @Override
        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
            super.onAttachedToRecyclerView(recyclerView);
        }


        public static class UserViewHolder extends RecyclerView.ViewHolder {
            CardView cardView;
            TextView name_entry;

            public UserViewHolder(View itemView) {
                super(itemView);
                cardView = (CardView) itemView.findViewById(R.id.user_layout);
                name_entry = (TextView) itemView.findViewById(R.id.name_entry);
             }
        }

        public interface OnCardClickListner {
            void OnCardClicked(View view, int position);
        }

        public void setOnCardClickListner(OnCardClickListner onCardClickListner) {
            this.onCardClickListner = onCardClickListner;
        }
    }

После этого вы получите этот метод переопределения в своей деятельности.

@Override
    public void OnCardClicked(View view, int position) {
        Log.d("OnClick", "Card Position" + position);
    }
10
  • 5
    Интересно, если пользователь прокручивает вверх и вниз, OnClickListener будет создавать и перерабатывать снова и снова, это эффективно?
    Vinh.TV
    10 июн.
  • Чтобы получить обновленное положение карты, эту второстепенную вещь можно скомпрометировать. 10 июн.
  • привет друг, приятно его рабочий шарм, у меня изначально есть действие по требованию с категориями и продуктами, если пользователь выбирает значок категории, мне нужно отображать категории в диалоговом окне или активности, если пользователь выбирает любую категорию, основанную на загрузке отфильтрованных данных для первого действия, пожалуйста, помогите мне как решить
    Harsha
    30 авг.
  • 1-й: вам нужно добавить метод с именем OnProductClicked в интерфейс OnCardClickListner, подобный этому общедоступному интерфейсу OnCardClickListner {void OnCardClicked (View view, int position); void OnProductClicked (представление просмотра, положение int); } 2-й: Затем установите прослушиватель кликов на значок вашего продукта, как это. Holder.yourProductIcon.setOnClickListener (новый View.OnClickListener () {@Override public void onClick (View v) {onCardClickListner.OnProductClicked (v, position);}}); 01 сен '16 в 5:48
  • Я не могу переопределить OnCardClicked. Как люди поддерживают этот ответ? 20 сен '16 в 7:26
21

> Чем RecyclerView отличается от Listview?

Одно отличие состоит в том, что есть LayoutManagerкласс с RecyclerView, с помощью которого вы можете управлять своим RecyclerViewподобным -

Horizontal or Vertical scrolling by LinearLayoutManager

GridLayout by GridLayoutManager

Staggered GridLayout by StaggeredGridLayoutManager

Как для горизонтальной прокрутки для RecyclerView-

LinearLayoutManager llm = new LinearLayoutManager(context);
llm.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(llm);
0
15

Как собрать все вместе, пример ...

  • onClick () обработка
  • Курсор - RecyclerView
  • Типы ViewHolder

    public class OrderListCursorAdapter extends CursorRecyclerViewAdapter<OrderListCursorAdapter.ViewHolder> {
    
    private static final String TAG = OrderListCursorAdapter.class.getSimpleName();
    private static final int ID_VIEW_HOLDER_ACTUAL = 0;
    private static final int ID_VIEW_HOLDER = 1;
    
    public OrderListCursorAdapter(Context context, Cursor cursor) {
        super(context, cursor);
    }
    
    public static class ViewHolderActual extends ViewHolder {
        private static final String TAG = ViewHolderActual.class.getSimpleName();
        protected IViewHolderClick listener;
        protected Button button;
    
        public ViewHolderActual(View v, IViewHolderClick listener) {
            super(v, listener);
            this.listener = listener;
            button = (Button) v.findViewById(R.id.orderList_item_button);
            button.setOnClickListener(this);
        }
    
        public void initFromData(OrderData data) {
            Log.d(TAG, "><initFromData(data=" + data + ")");
            orderId = data.getId();
            vAddressStart.setText(data.getAddressStart());
            vAddressEnd.setText(data.getAddressEnd());
        }
    
        @Override
        public void onClick(View view) {
            if (view instanceof Button) {
                listener.onButtonClick((Button) view, getPosition(), this);
            } else {
                super.onClick(view);
            }
        }
    
        public interface IViewHolderClick extends ViewHolder.IViewHolderClick {
            public void onButtonClick(Button button, int position, ViewHolder viewHolder);
        }
    }
    
    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        private static final String TAG = ViewHolder.class.getSimpleName();
        protected long orderId;
        protected IViewHolderClick listener;
        protected TextView vAddressStart;
        protected TextView vAddressEnd;
        protected TextView vStatus;
    
        public ViewHolder(View v, IViewHolderClick listener) {
            super(v);
            this.listener = listener;
            v.setOnClickListener(this);
    
            vAddressStart = (TextView) v.findViewById(R.id.addressStart);
            vAddressEnd = (TextView) v.findViewById(R.id.addressEnd);
            vStatus = (TextView) v.findViewById(R.id.status);
        }
    
        public void initFromData(OrderData data) {
            Log.d(TAG, "><initFromData(data=" + data + ")");
            orderId = data.getId();
            vAddressStart.setText(data.getAddressStart());
            vAddressEnd.setText(data.getAddressEnd());
        }
    
        public long getOrderId() {
            return orderId;
        }
    
        @Override
        public void onClick(View view) {
            listener.onCardClick(view, getPosition(), this);
        }
    
        public interface IViewHolderClick {
            public void onCardClick(View view, int position, ViewHolder viewHolder);
        }
    }
    
    @Override
    public int getItemViewType(int position) {
        return position == 0 ? ID_VIEW_HOLDER_ACTUAL : ID_VIEW_HOLDER;
    }
    
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.d(TAG, ">>onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")");
    
        ViewHolder result;
    
        switch (viewType) {
            case ID_VIEW_HOLDER_ACTUAL: {
                View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout_actual, parent, false);
                result = new ViewHolderActual(itemView, new ViewHolderActual.IViewHolderClick() {
                    @Override
                    public void onCardClick(View view, int position, ViewHolder viewHolder) {
                        Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
                        Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
                        intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
                        view.getContext().startActivity(intent);
                    }
    
                    @Override
                    public void onButtonClick(Button button, int position, ViewHolder viewHolder) {
                        Log.d(TAG, "><onButtonClick(button=" + button + ", position=" + position + ", viewHolder=" + viewHolder + ")");
                        Intent intent = new Intent(button.getContext(), OrderMapActivity.class);
                        intent.putExtra(OrderMapActivity.ARG_ORDER_ID, viewHolder.getOrderId());
                        button.getContext().startActivity(intent);
                    }
                });
                break;
            }
            case ID_VIEW_HOLDER:
            default: {
                View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_layout, parent, false);
                result = new ViewHolder(itemView, new ViewHolder.IViewHolderClick() {
                    @Override
                    public void onCardClick(View view, int position, ViewHolder viewHolder) {
                        Log.d(TAG, "><onCardClick(view=" + view + ", position=" + position + ", viewHolder=" + viewHolder + ")");
                        Intent intent = new Intent(view.getContext(), OrderDetailActivity.class);
                        intent.putExtra(OrderDetailActivity.ARG_ORDER_ID, viewHolder.getOrderId());
                        view.getContext().startActivity(intent);
                    }
                });
                break;
            }
        }
    
        Log.d(TAG, "<<onCreateViewHolder(parent=" + parent + ", viewType=" + viewType + ")= " + result);
        return result;
    }
    
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, Cursor cursor) {
        Log.d(TAG, "><onBindViewHolder(viewHolder=" + viewHolder + ", cursor=" + cursor + ")");
        final OrderData orderData = new OrderData(cursor);
        viewHolder.initFromData(orderData);
    }
    }
    
0
15

Следуя за отличным решением RxJava от MLProgrammer-CiM

Потребляйте / наблюдайте за кликами

Consumer<String> mClickConsumer = new Consumer<String>() {
        @Override
        public void accept(@NonNull String element) throws Exception {
            Toast.makeText(getApplicationContext(), element +" was clicked", Toast.LENGTH_LONG).show();
        }
    };

ReactiveAdapter rxAdapter = new ReactiveAdapter();
rxAdapter.getPositionClicks().subscribe(mClickConsumer);

RxJava 2. +

Измените исходный tl; dr как:

public Observable<String> getPositionClicks(){
    return onClickSubject;
}

PublishSubject#asObservable()был удален. Просто верните PublishSubjectфайл Observable.

1
  • Откуда у вас mClickConsumer во второй строке, пожалуйста, объясните немного. rxAdapter.getPositionClicks (). subscribe (mClickConsumer); 18 авг.
12

Насколько я понимаю ответ MLProgrammer-CiM , просто это можно сделать:

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    private ImageView image;
    private TextView title;
    private TextView price;

    public MyViewHolder(View itemView) {
        super(itemView);
        image = (ImageView)itemView.findViewById(R.id.horizontal_list_image);
        title = (TextView)itemView.findViewById(R.id.horizontal_list_title);
        price = (TextView)itemView.findViewById(R.id.horizontal_list_price);
        image.setOnClickListener(this);
        title.setOnClickListener(this);
        price.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        Toast.makeText(context, "Item click nr: "+getLayoutPosition(), Toast.LENGTH_SHORT).show();
    }
}
0
10

После прочтения ответа @ MLProgrammer-CiM вот мой код:

class NormalViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

    @Bind(R.id.card_item_normal)
    CardView cardView;

    public NormalViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
        cardView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if(v instanceof CardView) {
            // use getAdapterPosition() instead of getLayoutPosition()
            int itemPosition = getAdapterPosition();
            removeItem(itemPosition);
        }
    }
}
1
  • Остерегайтесь того, что getAdapterPositionможет вернуться -1за «без позиции». 26 апр '20 в 13:27
10

Я сделал это очень просто:

Просто добавьте 1 строку для позиции Clicked RecyclerView :

int position = getLayoutPosition()

Полный код для класса ViewHolder :

private class ChildViewHolder extends RecyclerView.ViewHolder {
        public ImageView imageView;
        public TextView txtView;

        public ChildViewHolder(View itemView) {
            super(itemView);
            imageView= (ImageView)itemView.findViewById(R.id.imageView);
            txtView= (TextView) itemView.findViewById(R.id.txtView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.i("RecyclerView Item Click Position", String.valueOf(getLayoutPosition()));
                }
            });
        }
    }

Надеюсь, что это поможет вам.

1
  • Вы должны использовать getAdapterPositionвместо getLayoutPosition. Остерегайтесь -1. 26 апр '20 в 13:26
9

RecyclerView не имеет, onItemClickListenerпотому что RecyclerView отвечает за переработку представлений (сюрприз!), Поэтому переработанное представление отвечает за обработку получаемых им событий щелчка.

На самом деле это значительно упрощает использование, особенно если у вас есть элементы, на которые можно щелкнуть в нескольких местах.


В любом случае, обнаружить щелчок по элементу RecyclerView очень просто. Все, что вам нужно сделать, это определить интерфейс (если вы не используете Kotlin, и в этом случае вы просто передаете лямбду):

public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
    private final Clicks clicks;

    public MyAdapter(Clicks clicks) {
        this.clicks = clicks;
    }

    private List<MyObject> items = Collections.emptyList();

    public void updateData(List<MyObject> items) {
        this.items = items;
        notifyDataSetChanged(); // TODO: use ListAdapter for diffing instead if you need animations
    }

    public interface Clicks {
        void onItemSelected(MyObject myObject, int position);
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        private MyObject myObject;    

        public MyViewHolder(View view) {
            super(view);
            // bind views
            view.setOnClickListener((v) -> {
                int adapterPosition = getAdapterPosition();
                if(adapterPosition >= 0) {
                    clicks.onItemSelected(myObject, adapterPosition);
                }
            });
        }

        public void bind(MyObject myObject) {
            this.myObject = myObject;
            // bind data to views
        }
    }
}

Тот же код в Котлине:

class MyAdapter(val itemClicks: (MyObject, Int) -> Unit): RecyclerView.Adapter<MyViewHolder>() {
    private var items: List<MyObject> = Collections.emptyList()

    fun updateData(items: List<MyObject>) {
        this.items = items
        notifyDataSetChanged() // TODO: use ListAdapter for diffing instead if you need animations
    }

    inner class MyViewHolder(val myView: View): RecyclerView.ViewHolder(myView) {
        private lateinit var myObject: MyObject

        init {
            // binds views
            myView.onClick {
                val adapterPosition = getAdapterPosition()
                if(adapterPosition >= 0) {
                    itemClicks.invoke(myObject, adapterPosition)
                }
            }
        }

        fun bind(myObject: MyObject) {
            this.myObject = myObject
            // bind data to views
        }
    }
}

То, что вам НЕ нужно делать:

1.) вам не нужно вручную перехватывать сенсорные события

2.) вам не нужно возиться с дочерними слушателями изменения состояния присоединения

3.) вам не нужен PublishSubject / PublishRelay из RxJava

Просто используйте прослушиватель кликов.

2
  • В видео нет обработки onClick? 15 сен '20 в 18:12
  • Касса 0:45 в видео есть onClickListener, прикрепленный к адаптеру 16 сен '20 в 20:08
8

Я использую этот метод для запуска намерения из RecyclerView:

@Override
 public void onBindViewHolder(ViewHolder viewHolder, int i) {

    final MyClass myClass = mList.get(i);
    viewHolder.txtViewTitle.setText(myclass.name);
   ...
    viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
      @Override
       public void onClick(View v){
             Intent detailIntent = new Intent(mContext, type.class);                                                            
             detailIntent.putExtra("MyClass", myclass);
             mContext.startActivity(detailIntent);
       }
}
);
1
  • Это нехороший способ. Потому onBindViewHolder(...)что не вызывается после notify()методов. Таким образом, в некоторых ситуациях вы можете получить неправильную позицию щелчка. 29 дек.
5

Посмотрите мой подход к этому:

Сначала объявите такой интерфейс:

/**
 * Interface used for delegating item click events in a {@link android.support.v7.widget.RecyclerView}
 * Created by Alex on 11/28/2015.
 */
  public interface OnRecyclerItemClickListener<T> {

     /**
      * Called when a click occurred inside a recyclerView item view
      * @param view that was clicked
      * @param position of the clicked view
      * @param item the concrete data that is displayed through the clicked view
      */
      void onItemClick(View view, int position, T item);
   }

Затем создайте адаптер:

public class CustomRecyclerAdapter extends RecyclerView.Adapter {      

    private class InternalClickListener implements View.OnClickListener{

      @Override
      public void onClick(View v) {
        if(mRecyclerView != null && mItemClickListener != null){
            // find the position of the item that was clicked
            int position = mRecyclerView.getChildAdapterPosition(v);
            Data data = getItem(position);
            // notify the main listener
            mItemClickListener.onItemClick(v, position, data);
        }
    }
}

private final OnRecyclerItemClickListener mItemClickListener;
private RecyclerView mRecyclerView;    
private InternalClickListener mInternalClickListener;


/**
 *
 * @param itemClickListener used to trigger an item click event
 */
public PlayerListRecyclerAdapter(OnRecyclerItemClickListener itemClickListener){        
    mItemClickListener = itemClickListener;
    mInternalClickListener = new InternalClickListener();
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false);

    v.setOnClickListener(mInternalClickListener);

    ViewHolder viewHolder = new ViewHolder(v);
    return viewHolder;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    // do your binding here
}

@Override
public int getItemCount() {
    return mDataSet.size();
}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);

    mRecyclerView = recyclerView;
}

@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
    super.onDetachedFromRecyclerView(recyclerView);

    mRecyclerView = null;
}

public Data getItem(int position){
    return mDataset.get(position);
}
}

А теперь посмотрим, как это интегрировать из фрагмента:

public class TestFragment extends Fragment implements OnRecyclerItemClickListener<Data>{
   private RecyclerView mRecyclerView;

   @Override
   public void onItemClick(View view, int position, Data item) {
     // do something
   }

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      return inflater.inflate(R.layout.test_fragment, container, false);
   }

   @Override
   public void onViewCreated(View view, Bundle savedInstanceState) {
      mRecyclerView = view.findViewById(idOfTheRecycler);
      mRecyclerView .setAdapter(new CustomRecyclerAdapter(this));
   }
0
5

Если вы хотите добавить onClick () к дочернему представлению элементов, например, кнопку в элементе, я обнаружил, что вы можете легко сделать это в onCreateViewHolder () вашего собственного RecyclerView.Adapter примерно так:

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v = LayoutInflater
                    .from(parent.getContext())
                    .inflate(R.layout.cell, null);

            Button btn = (Button) v.findViewById(R.id.btn);
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //do it
                }
            });

            return new MyViewHolder(v);
        }

я не знаю, хороший ли это способ, но он работает хорошо. Если у кого-то есть идея получше, очень рад подсказать и поправить свой ответ! :)

0
5

Вот способ довольно легко реализовать это, если у вас есть список POJO и вы хотите получить его одним щелчком мыши за пределами адаптера.

В своем адаптере создайте прослушиватель для событий щелчка и метод для его установки:

public class MyAdapter extends RecyclerView.Adapter<SitesListAdapter.ViewHolder> {
...
private List<MyPojo> mMyPojos;
private static OnItemClickListener mOnItemClickListener;

...
public interface OnItemClickListener {
    public void onItemClick(MyPojo pojo);
}

...
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
    mOnItemClickListener = onItemClickListener;
}
...

}

В вашем ViewHolder реализуйте onClickListener и создайте член класса для временного хранения POJO, представляемого представлением, таким образом (это пример, создание установщика было бы лучше):

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    public MyPojo mCurrentPojo;
    ...
    public ViewHolder(View view) {
        super(v);
        ...
        view.setOnClickListener(this); //You could set this on part of the layout too
    }

    ...
    @Override
    public void onClick(View view) {
        if(mOnItemClickListener != null && mCurrentPojo != null){
            mOnItemClickListener.onItemClick(mCurrentPojo);
        }
    }

Вернувшись в свой адаптер, установите текущий POJO, когда ViewHolder привязан (или значение null, если у текущего представления его нет):

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    final MyPojo currentPojo = mMyPojos.get(position); 
    holder.mCurrentPojo = currentPojo;
    ...

Вот и все, теперь вы можете использовать его так из своего фрагмента / активности:

    mMyAdapter.setOnItemClickListener(new mMyAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(MyPojo pojo) {
            //Do whatever you want with your pojo here
        }
    });
4
  • вы пытаетесь реализовать интерфейс, как если бы это был метод объекта. Это не сработает
    Akshay
    22 ноя '16 в 22: 382016-11-22 19:38
  • Обновление: вам нужно mMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});
    Akshay
    22 ноя '16 в 21:02
  • @Akshay Извините, но я думаю, что мой код правильный. Вы пробовали? Все слушатели на Android следуют тому же принципу, и вам нужно создать экземпляр интерфейса с помощью «нового» (например, stackoverflow.com/questions/4709870/… )
    Simon
    23 ноя '16 в 11:52
  • Слушатель не должен быть статичным. 26 апр '20 в 13:26
4

Да, ты можешь

public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {

    //inflate the view 

    View view = LayoutInflator.from(parent.getContext()).inflate(R.layout.layoutID,null);

    ViewHolder holder = new ViewHolder(view);

    //here we can set onClicklistener
    view.setOnClickListener(new  View.OnClickListeener(){
        public void onClick(View v)
        {
            //action
        }
    });

return holder;
0
3

Это сработало для меня:

@Override
public void onBindViewHolder(PlacesListViewAdapter.ViewHolder holder, int position) {
    ----
    ----
    ----
    // Set setOnClickListener(holder);
}


@Override
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    ----
    ----
    ----

    @Override
    public void onClick(View view) {
        // Use to get the item clicked getAdapterPosition()
    }
}
2
  • Это не хорошая настройка пути setOnClickListener(holder)в onBindViewHolder(). Потому что onBindViewHolder (...) не вызывается после методов notify (). Таким образом, в некоторых ситуациях вы можете получить неправильную позицию щелчка. 29 дек.
  • Вы должны создать прослушиватель кликов внутри onCreateViewHolder. 26 апр '20 в 13:26
3

Здесь вы можете обрабатывать несколько onclick, см. Код ниже, и это очень эффективно

    public class RVNewsAdapter extends RecyclerView.Adapter<RVNewsAdapter.FeedHolder> {

    private Context context;
    List<News> newsList;
    // Allows to remember the last item shown on screen
    private int lastPosition = -1;

    public RVNewsAdapter(List<News> newsList, Context context) {
        this.newsList = newsList;
        this.context = context;
    }

    public static class FeedHolder extends RecyclerView.ViewHolder implements OnClickListener {

        ImageView img_main;
        TextView tv_title;
        Button bt_facebook, bt_twitter, bt_share, bt_comment;


        public FeedHolder(View itemView) {
            super(itemView);

            img_main = (ImageView) itemView.findViewById(R.id.img_main);
            tv_title = (TextView) itemView.findViewById(R.id.tv_title);
            bt_facebook = (Button) itemView.findViewById(R.id.bt_facebook);
            bt_twitter = (Button) itemView.findViewById(R.id.bt_twitter);
            bt_share = (Button) itemView.findViewById(R.id.bt_share);
            bt_comment = (Button) itemView.findViewById(R.id.bt_comment);

            img_main.setOnClickListener(this);
            bt_facebook.setOnClickListener(this);
            bt_twitter.setOnClickListener(this);
            bt_comment.setOnClickListener(this);
            bt_share.setOnClickListener(this);

        }


        @Override
        public void onClick(View v) {

            if (v.getId() == bt_comment.getId()) {

                Toast.makeText(v.getContext(), "Comment  " , Toast.LENGTH_SHORT).show();

            } else if (v.getId() == bt_facebook.getId()) {

                Toast.makeText(v.getContext(), "Facebook  " , Toast.LENGTH_SHORT).show();

            } else if (v.getId() == bt_twitter.getId()) {

                Toast.makeText(v.getContext(), "Twitter  " , Toast.LENGTH_SHORT).show();

            } else if (v.getId() == bt_share.getId()) {

                Toast.makeText(v.getContext(), "share  " , Toast.LENGTH_SHORT).show();

            }
            else {
                Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }

    @Override
    public FeedHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.feed_row, parent, false);
        FeedHolder feedHolder = new FeedHolder(view);

        return feedHolder;
    }

    @Override
    public void onBindViewHolder(FeedHolder holder, int position) {

        holder.tv_title.setText(newsList.get(position).getTitle());


        // Here you apply the animation when the view is bound
        setAnimation(holder.img_main, position);
    }

    @Override
    public int getItemCount() {
        return newsList.size();
    }


    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position) {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition) {
            Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }


}
1
  • Он имел в виду itemView. 11 окт.
3

Изменил свой комментарий ...

public class MyViewHolder extends RecyclerView.ViewHolder {

        private Context mContext;

        public MyViewHolder(View itemView) {
            super(itemView);

            mContext = itemView.getContext();

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    int itemPosition = getLayoutPosition();
                    Toast.makeText(mContext, "" + itemPosition, Toast.LENGTH_SHORT).show();

                }
            });
        }
2
  • 1
    Это предложение, но лучше, если вы удалите этот ответ, если только многие люди не проголосуют отрицательно. Это решение окончательно неверно, оно работает, но плохо программирует. Прочтите несколько блогов о RecyclerView и изучите некоторые библиотеки адаптеров, многие из них уже действуют в Github, например, FlexibleAdapter, FastAdapter и так далее ... 11 окт.
  • 1
    Ваш ответ является относительно новым из длинного списка ответов с высокими оценками и находится внизу страницы, поэтому за него все еще осталось 0 голосов. Кстати, в новом коде вы должны использовать getAdapterPosition(). 13 окт.
2

Отметьте этот, в котором я все правильно реализовал

Класс RecyclerViewHolder

public class RecyclerViewHolder extends RecyclerView.ViewHolder  {

    //view holder is for girdview as we used in the listView
    public ImageView imageView,imageView2;
    public RecyclerViewHolder(View itemView) {
        super(itemView);
        this.imageView=(ImageView)itemView.findViewById(R.id.image);
    }

}

Адаптер

public class RecyclerView_Adapter extends RecyclerView.Adapter<RecyclerViewHolder> {

    //RecyclerView will extend to recayclerview Adapter
    private ArrayList<ModelClass> arrayList;
    private Context context;
    private static RecyclerViewClickListener itemListener;
    //constructor of the RecyclerView Adapter
    RecyclerView_Adapter(Context context,ArrayList<ModelClass> arrayList,RecyclerViewClickListener itemListener){
        this.context=context;
        this.arrayList=arrayList;
        this.itemListener=itemListener;
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //this method will inflate the custom layout and return as viewHolder
        LayoutInflater layoutInflater=LayoutInflater.from(parent.getContext());
        ViewGroup mainGroup=(ViewGroup) layoutInflater.inflate(R.layout.single_item,parent,false);
        RecyclerViewHolder listHolder=new RecyclerViewHolder(mainGroup);

        return listHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerViewHolder holder, final int position) {

        final ModelClass modelClass=arrayList.get(position);
        //holder
        RecyclerViewHolder mainHolder=(RecyclerViewHolder)holder;
        //convert the drawable image into bitmap
        Bitmap image= BitmapFactory.decodeResource(context.getResources(),modelClass.getImage());
        //set the image into imageView
        mainHolder.imageView.setImageBitmap(image);
        //to handle on click event when clicked on the recyclerview item and
        // get it through the RecyclerViewHolder class we have defined the views there
        mainHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //get the position of the image which is clicked
             itemListener.recyclerViewListClicked(v,position);
            }
        });

    }

    @Override
    public int getItemCount() {
        return (null!=arrayList?arrayList.size():0);
    }
}

Интерфейс

public interface RecyclerViewClickListener {

    //this is method to handle the event when clicked on the image in Recyclerview
    public void recyclerViewListClicked(View v,int position);
}

//and to call this method in activity
RecyclerView_Adapter adapter=new RecyclerView_Adapter(Wallpaper.this,arrayList,this);
        recyclerView.setAdapter(adapter);
        adapter.notifyDataSetChanged();


    @Override
    public void  recyclerViewListClicked(View v,int position){

        imageView.setImageResource(wallpaperImages[position]);

    }
0
2

Получите доступ к mainViewиз rowLayout(cell)для вас RecyclerViewи OnBindViewHolderнапишите в своем коде:

    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        Movie movie = moviesList.get(position);
        holder.mainView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                System.out.println("pos " + position);
            }
        });
    }
0
2

it worked for me. Hope it will help. Most simplest way.

Держатель вида изнутри

class GeneralViewHolder extends RecyclerView.ViewHolder {
    View cachedView = null;

    public GeneralViewHolder(View itemView) {
        super(itemView);
        cachedView = itemView;
    }

Внутри OnBindViewHolder ()

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
            final GeneralViewHolder generalViewHolder = (GeneralViewHolder) holder;
            generalViewHolder.cachedView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(context, "item Clicked at "+position, Toast.LENGTH_SHORT).show();
                }
            });

И дайте мне знать, у вас есть вопросы по этому решению?

1
  • 1
    Вы должны установить onClickListener onCreateViewHolderдля лучшей производительности, но тогда вам нужно получить позицию, используя getAdapterPosition()(и защититься от -1) 26 апр '20 в 13:23
1

Вместо реализации интерфейса View.OnClickListener внутри держателя представления или создания интерфейса и реализации интерфейса в вашей деятельности .. Я использовал этот код для простой реализации OnClickListener.

public static class SimpleStringRecyclerViewAdapter
            extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {

        // Your initializations goes here...
        private List<String> mValues;

        public static class ViewHolder extends RecyclerView.ViewHolder {

            //create a variable mView
            public final View mView;

            /*All your row widgets goes here
            public final ImageView mImageView;
            public final TextView mTextView;*/

            public ViewHolder(View view) {
                super(view);
                //Initialize it here
                mView = view;

                /* your row widgets initializations goes here
                mImageView = (ImageView) view.findViewById(R.id.avatar);
                mTextView = (TextView) view.findViewById(android.R.id.text1);*/
            }
        }

        public String getValueAt(int position) {
            return mValues.get(position);
        }

        public SimpleStringRecyclerViewAdapter(Context context, List<String> items) {

            mBackground = mTypedValue.resourceId;
            mValues = items;
        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.list_item, parent, false);
            view.setBackgroundResource(mBackground);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(final ViewHolder holder, int position) {
            holder.mBoundString = mValues.get(position);
            holder.mTextView.setText(mValues.get(position));

            //Here it is simply write onItemClick listener here
            holder.mView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Context context = v.getContext();
                    Intent intent = new Intent(context, ExampleActivity.class);

                    context.startActivity(intent);
                }
            });
        }

        @Override
        public int getItemCount() {
            return mValues.size();
        }
    }
2
  • 2
    И вы думаете, что создание нового объекта OnItemClickListener при каждом вызове метода onBind () - это хороший подход? 27 ноя.
  • Молодец. Работает отлично!
    sam
    28 фев '19 в 11:55
1

использовать PlaceHolderView

@Layout(R.layout.item_view_1)
public class View1{

    @View(R.id.txt)
    public TextView txt;

    @Resolve
    public void onResolved() {
        txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
    }

    @Click(R.id.btn)
    public void onClick(){
        txt.setText(String.valueOf(System.currentTimeMillis() / 1000));
    }
}
1
  • Мне нравится решение, оно выглядит аккуратно и идеально соответствует библиотеке Pertusin, которую я использую в настоящее время. 25 апр в 0:08
1
 main_recyclerview.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
        {
            int position=rv.getChildAdapterPosition(rv.findChildViewUnder(e.getX(),e.getY()));

            switch (position)
            {
                case 0:
                {
                    wifi(position);
                    adapter2.notifyDataSetChanged();
                }
                break;

                case 1:
                {
                    sound(position);
                    adapter2.notifyDataSetChanged();
                }
                break;

                case 2:
                {
                    bluetooth(position);
                    adapter2.notifyDataSetChanged();
                }
                break;


            }
            return true;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e)
        {

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        }
    });
1
  • Возникает вопрос: «Почему в RecyclerView нет onItemClickListener ()?» НЕ «Как реализовать onItemClickListener () в RecyclerView?» . 7 окт.
0

Я написал библиотеку для обработки события щелчка на элементе просмотра android recycler. Вы можете найти весь учебник в https://github.com/ChathuraHettiarachchi/RecycleClick

RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemClickListener(new RecycleClick.OnItemClickListener() {
            @Override
            public void onItemClicked(RecyclerView recyclerView, int position, View v) {
                // YOUR CODE
            }
        });

или для обработки долгого нажатия элемента вы можете использовать

RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
                // YOUR CODE
                return true;
            }
        });
1
  • Сэр, я пробовал вашу библиотеку, но столкнулся с проблемой с maven? 19 мая '17 в 4:52
0

Reclerview анимация не тестировалась, остальные в норме. Думаю, он оптимизирован по максимуму. Интерфейс имеет и другое применение, которое можно временно игнорировать.

public abstract class BaseAdapterRV<VH extends BaseViewHolder> extends RecyclerView.Adapter<VH> implements AdapterInterface {
    public final String TAG = getClass().getSimpleName();

    protected final Activity mActivity;
    protected final LayoutInflater mInflater;
    protected ItemClickInterface<?, Integer> mListener;

    public BaseAdapterRV(Activity activity) {
        mActivity = activity;
        mInflater = LayoutInflater.from(mActivity);
    }

    @Override
    public final VH onCreateViewHolder(ViewGroup parent, int viewType) {
        return onCreateViewHolder(parent, viewType, mInflater);
    }

    @Override
    public final void onBindViewHolder(VH holder, int position) {
        holder.itemView.setTag(R.id.tag_view_click, position);
        //创建点击事件
        holder.itemView.setOnClickListener(mListener);
        holder.itemView.setOnLongClickListener(mListener);
        onBindVH(holder, position);
    }


    ///////////////////////////////////////////////////////////////////////////
    // 以下是增加的方法
    ///////////////////////////////////////////////////////////////////////////

    /**
     * 注意!涉及到notifyItemInserted刷新时立即获取position可能会不正确
     * 里面也有onItemLongClick
     */
    public void setOnItemClickListener(ItemClickInterface<?, Integer> listener) {
        mListener = listener;
        notifyDataSetChanged();
    }

    @NonNull
    protected abstract VH onCreateViewHolder(ViewGroup parent, int viewType, LayoutInflater inflater);

    protected abstract void onBindVH(VH holder, int position);

}

Это интерфейс

/**
 * OnItemClickListener的接口
 * 见子类实现{@link OnItemClickListener}{@link OnItemItemClickListener}
 */
public interface ItemClickInterface<DATA1, DATA2> extends View.OnClickListener, View.OnLongClickListener {

    void onItemClick(DATA1 data1, DATA2 data2);

    boolean onItemLongClick(DATA1 data1, DATA2 data2);
}

Это абстрактный класс

public abstract class OnItemClickListener<DATA> implements ItemClickInterface<View, DATA> {
    @Override
    public void onClick(View v) {
        onItemClick(v, (DATA) v.getTag(R.id.tag_view_click));
    }

    @Override
    public boolean onLongClick(View v) {
        return onItemLongClick(v, (DATA) v.getTag(R.id.tag_view_click));
    }

    @Override
    public boolean onItemLongClick(View view, DATA data) {
        return false;
    }
}

Тебе только это нужно

    mAdapter.setOnItemClickListener(new OnItemClickListener<Integer>() {
        @Override
        public void onItemClick(View view, Integer integer) {

        }

        @Override
        public boolean onItemLongClick(View view, Integer integer) {
            return true;
        }
    });
2
  • BaseViewHolder как RecyclerView.ViewHolder
    王 能
    16 ноя '17 в 6:29
  • R.id.tag_view_click требует, чтобы вы объявили в каталоге значений <item type = "id" name = "tag_view_click" />
    王 能
    16 ноя '17 в 6:35
0

Я нашел один из самых коротких способов использования изменяемых живых данных жизненного цикла androidx

Адаптер:

private val onItemClickListener = MutableLiveData<YourAdapterItem>()


override fun onBindViewHolder(holder: GifsViewHolder, position: Int) {
    holder.itemView.setOnClickListener { onItemClickListener.value = gifs[position] }
}
fun getOnItemClickListener(): MutableLiveData<Gif> {
    return onItemClickListener
}

в любом месте MainActivity

    yourFancyAdapter.getOnItemClickListener().observe(this, Observer {
        println(it)
    })
0