Чому RecyclerView не має OnItemClickListener ()?


965

Я досліджував, RecyclerViewі я був здивований, побачивши, що RecyclerViewцього немає onItemClickListener().

У мене два питання.

Основне питання

Я хочу знати, чому Google видалено 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) {

    }
}

Це нормально / чи є кращий спосіб?


31
Щоб ваш код працював, потрібно додати itemLayoutView.setOnClickListener (це); в конструкторі ViewHolder.
Уїтніленд

3
Ви задали гарне запитання ... у багатьох розробників виникають сумніви щодо RecyclerView та ListView.
venkat

17
Питання: Чому RecyclerView не має OnItemClickListner ()? Усі відповіді нижче, окрім 2: Як додати onclick для RecyclerView
Манохар Редді

2
Бажаєте, щоб хтось фактично взяв час, щоб відповістиQUESTION
Ojonugwa Jude Ochalifu

1
Я думаю, що їх зняли через проблеми з продуктивністю та пам'яттю. Але подивіться на прийняту відповідь .... це призведе до тих же проблем. Я думаю, що ви помилилися, прийнявши це, і тепер інші ppls роблять помилки через вас :).
Алекс

Відповіді:


1207

tl; dr 2016 Використовуйте RxJava та PublishSubject, щоб відкрити спостережуване для кліків.

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 нарешті це визнає.


78
RecyclerView.ViewHolder має метод getPosition (). З деякими коригуваннями цього коду ви можете передати позицію слухачеві.
se_bastiaan

13
Він повністю пропустив те onBindViewHolder(holder, position), де саме повинна бути заповнена інформація.
MLProgrammer-CiM

26
@se_bastiaan getPosition()застаріло "Цей метод застарілий, оскільки його значення неоднозначне через асинхронну * обробку оновлень адаптера. Будь ласка, використовуйте {@link #getLayoutPosition ()} або * {@link #getAdapterPosition ()} залежно від вашого випадку використання. "
upenpat

8
У вас є кілька варіантів. Помістивши позицію в полі int всередині програми перегляду, оновіть її під час прив’язки, а потім поверніть її на клацання.
MLProgrammer-CiM

8
@ MLProgrammer-CiM "У вас є кілька варіантів. Помістивши позицію в полі int всередині оглядача, оновіть його під час прив'язки, а потім поверніть його на натискання - це одне." ViewHolder вже має це значення і вже встановлено. Просто зателефонуйтеviewHolder.getPosition();
Чад Бінгем

104

Чому RecyclerView не має onItemClickListener

Це RecyclerViewінструментальна панель, на відміну від старої, ListViewвона має меншу кількість функцій та більшу гнучкість. Це onItemClickListenerне єдина функція, яка видаляється з ListView. Але у нього є безліч слухачів і спосіб поширити його на свій смак, він набагато потужніший у правильних руках;).

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

Якщо ви хочете дізнатися, які ще цікаві функції RecyclerViewдодано, прочитайте цю відповідь на інше питання.

Пам'ять ефективна - рішення, що випадає для OnItemClickListener

Це рішення було запропоновано від Hugo Visser , в Android GDE, відразу після того, RecyclerViewбув звільнений. Він зробив доступним для вас клас без ліцензій, щоб просто скинути свій код і використовувати його.

Він демонструє деяку універсальність, впроваджену за RecyclerViewдопомогою використання RecyclerView.OnChildAttachStateChangeListener.

Редагувати 2019 : версію kotlin від мене, java one, від Hugo Visser, що міститься нижче

Котлін / Ява

Створіть файл 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 = this@ItemClickSupport.recyclerView.getChildViewHolder(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 ?: return@OnClickListener
        // 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 ?: return@OnLongClickListener false
        val holder = this.recyclerView.getChildViewHolder(v)
        return@OnLongClickListener 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-файл)

Бонусна особливість версії Котліна

Іноді ви не хочете, щоб усі елементи RecyclerView можна було натискати.

Для вирішення цього питання я представив ItemClickSupportViewHolderінтерфейс, який ви можете використовувати на своєму, ViewHolderщоб керувати, який елемент можна натискати.

Приклад:

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

Java

Використання:

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час прокрутки.

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

Заключне зауваження

Майте на увазі, що цілком прекрасно обробляти його у своєму адаптері, встановлюючи на кожному перегляді списку слухача кліків, як і інші запропоновані відповіді.

Це просто не найефективніша річ (ви створюєте нового слухача кожен раз, коли ви повторно використовуєте перегляд), але він працює, і в більшості випадків це не проблема.

Це також трохи проти роз'єднання проблем, тому що насправді робота адаптера не є делегуванням подій клацання.


addOnChildAttachStateChangeListener в конструкторі і ніколи не видаляючи його?
Саба

@Saba, ви не повинні використовувати конструктор безпосередньо, але за допомогою статичних методів addTo()і removeFrom(). Екземпляр асоціюється з тегом reciklierview і вмирає з ним або коли він видаляється вручну.
Даніеле Сегато

Стільки коду, коли все, що вам потрібно зробити, - це встановити слухач кліків onCreateViewHolderі все.
EpicPandaForce

Ваші аргументи витікають, що справа не в кількості коду, а в принципі ефективності та відповідальності, а кількість коду не є великою ознакою хорошого / поганого рішення, особливо коли цей код є багаторазовим та ефективним. Після того, як ви вкладете цей код у свій проект (який ви можете розглянути як включення до бібліотеки), все, що вам потрібно, - це 1-лайнер на вашому RecyclerView.
Даніеле Сегато

@DanieleSegato Дякую вам за це!
Містер Октодуд

90

Мені це подобається і я його використовую

Всередині

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

Я раніше читав, що є кращий спосіб, але мені подобається, що цей спосіб простий і не складний.


23
Мабуть, мабуть зробити MyOnClickListenerзмінною екземпляра, тому що ви створюватимете новий примірник кожного разу за допомогою newключового слова
user2968401,

14
recilerView.getChildPosition (v); тепер вимкнено, використовуйте recilerView. indexOfChild (v);
Кадір Хуссей

4
У вас немає проблем під час прокрутки? На моїх тестах itemPosition, здається, залежить від переробки поглядів, тому він йде не так у порівнянні з моїм повним списком в адаптері.
Саймон

16
Як потрапити recyclerViewв MyOnClickListener?
Гоо

3
Замість використання: int itemPosition = recilerView.indexOfChild (v); ви повинні використовувати: RecyclerView.ViewHolder держатель = recilerView.getChildViewHolder (v); int itemPosition = hold.getAdapterPosition ();
Гордон Фрімен

35

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

7
Не елегантно, якщо чесно.
vishal dharankar

@vishaldharankar чому б і ні? Я думаю, що це найкращий спосіб уникнути конфлікту зміни фокусу після використанняSwipeRefreshLayout
NickUnuchek

Це зовсім непотрібно, ви можете використовувати View.OnClickListener замість перехоплення дотику.
EpicPandaForce

23

Завдяки @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
    }
}

}

34
Це створить і збирає сміття тонни предметів під час прокрутки, замість цього слід створити один OnClickListener і повторно використовувати його
marmor

2
Також onBindViewHolder()називається багато разів для одного і того ж виду при прокрутці. Навіть створення синглу OnClickListenerне допоможе, тому що ви будете скидатися OnClickListenerщоразу, коли будете onBindViewHolder()викликано. Дивіться замість @ MLProgrammer-CiM рішення.
вовахост

Ви також можете покращити рішення @ MLProgrammer-CiM і замість того, щоб створити об'єкт слухача для кожного перегляду в цьому рядку, MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ....ви можете створити одного слухача для всіх представлень даних.
вовахост

21

Хлопці використовують цей код у вашій основній діяльності. Дуже ефективний метод

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

5
Цікаво, якщо користувач прокручує вгору і вниз, OnClickListener буде створювати & повторно переробляти знову і знову, це ефективно?
Vinh.TV

Щоб отримати оновлене положення картки, ця незначна річ може поставити під загрозу.
waqas ali

привіт друг, приємно його робочий шарм, у мене є вимога активності з категоріями та продуктами спочатку, якщо користувач вибирає значок категорії, мені потрібно відобразити категорії в діалоговому вікні або діяльності, якщо користувач вибирає будь-яку категорію, засновану на титанні завантаження відфільтрованих даних на першу діяльність, будь ласка, допоможіть мені як вирішити
Харша

1-й: Вам потрібно додати метод з назвою OnProductClicked в інтерфейс OnCardClickListner, як цей публічний інтерфейс OnCardClickListner {void OnCardClicked (Перегляд подання, позиція int); недійсний OnProductClicked (Перегляд подання, позиція int); } 2-е: Потім ввімкніть прослуховувач кліків на піктограму вашого продукту, як це. hold.yourProductIcon.setOnClickListener (новий View.OnClickListener () {@Override public void onClick (View v) {onCardClickListner.OnProductClicked (v, позиція);}});
waqas ali

Я не в змозі переотримати OnCardClicked. Як люди підтримують цю відповідь?
The_Martian

19

> Чим RecyclerView відрізняється від Listview?

Відмінність полягає в тому, що є LayoutManagerклас з RecyclerView , з допомогою якого ви можете управляти RecyclerViewякось

Горизонтальна або вертикальна прокрутка поLinearLayoutManager

GridLayout від GridLayoutManager

Поетапна GridLayout від StaggeredGridLayoutManager

Як і для горизонтальної прокрутки для RecyclerView-

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

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.


Де ви отримуєте mClickConsumer на другому рядку, якщо ви, будь ласка, поясніть трохи. rxAdapter.getPositionClicks (). підписатися (mClickConsumer);
bluetoothfx

14

Як скласти все це на прикладі ...

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

11

Наскільки я розумію відповідь 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();
    }
}

9

Прочитавши відповідь @ 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);
        }
    }
}

Остерігайтеся того, що getAdapterPositionможна повернутись -1за "без позиції".
EpicPandaForce

9

Я зробив це так, дуже просто:

Просто додайте 1 рядок для позиції натиснутого 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()));
                }
            });
        }
    }

Сподіваюся, що це вам допоможе.


Ви повинні використовувати getAdapterPositionзамість getLayoutPosition. Охороняйте проти -1.
EpicPandaForce

7

Я використовую цей метод для запуску наміру з 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);
       }
}
);

Це не гарний спосіб. Тому що onBindViewHolder(...)не викликається після notify()методів. Отже, ви можете отримати неправильну позицію клацання в деяких ситуаціях.
Алекс Кісель

5

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

Просто використовуйте слухач кліків.


4

Дивіться мій підхід до цього:

Спочатку заявіть про такий інтерфейс:

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

4

Якщо ви хочете додати 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);
        }

я не знаю, чи це хороший спосіб, але він працює добре. Якщо хтось має кращу ідею, дуже радий сказати мені та виправити свою відповідь! :)


4

Ось спосіб реалізувати це досить легко, якщо у вас є список 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 пов'язаний (або нульовий, якщо у поточному представленні його немає):

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

ви намагаєтеся реалізувати Інтерфейс так, ніби це був метод Object. Це не спрацює
Акшай

Оновлення: Вам потрібноmMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});
Акшай

@Akshay Вибачте, але я думаю, що мій код правильний. Ви пробували? Усі слухачі Android дотримуються того самого принципу, і вам потрібно створити інтерфейс «новим» (наприклад: stackoverflow.com/questions/4709870/… )
Simon

Слухач не повинен бути статичним.
EpicPandaForce

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;

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


}

Він мав на увазі itemView.
Девідеяс

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

                }
            });
        }

1
Це пропозиція, але краще, якщо ви видалите цю відповідь, якщо багато людей не проголосують негативно. Це рішення, безумовно, неправильне, воно працює, але воно погано програмує. Будь ласка, прочитайте кілька блогів для RecyclerView та вивчіть деякі бібліотеки адаптерів, у Github вже є багато дійсних, як-то гнучкийAdapter, FastAdapter і так далі ...
Davideas

1
Ви відповідаєте відносно новим із довгого списку відповідей з високою оцінкою та внизу сторінки, тому у нього все ще 0 голосів. До речі, у новому коді слід використовувати getAdapterPosition().
Давідеас

Вам просто потрібно прочитати javaDoc з 2-х методів.
Давідеас

3

Перевірте цю, в якій я реалізував усі речі належним чином

Клас 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]);

    }

2

Це працювало для мене:

@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()
    }
}

Це не хороша настройка шляху setOnClickListener(holder)в onBindViewHolder(). Тому що onBindViewHolder (...) не викликається після notify () методів. Отже, ви можете отримати неправильну позицію клацання в деяких ситуаціях.
Алекс Кісель

Ви повинні створити слухач кліків всередині onCreateViewHolder.
EpicPandaForce

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

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
І ви вважаєте, що створення нового об'єкта OnItemClickListener при кожному виклику методу onBind () - це хороший підхід?
Цирк Олександру

Хороша робота. Чудово працює!
сам

1

це працювало для мене. Сподіваюся, це допоможе. Найпростіший спосіб.

Внутрішній тримач виду

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

І дайте мені знати, чи є у вас запитання щодо цього рішення?


Ви повинні встановити onClickListener onCreateViewHolderдля кращої продуктивності, але тоді вам потрібно отримати позицію, використовуючи getAdapterPosition()(та -1
захистившись

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

        }
    });

використовуючи це, ви можете отримати точні результати ..................
dileep krishnan

Питання "Чому RecyclerView не має OnItemClickListener ()?" НЕ "Як реалізувати onItemClickListener () в RecyclerView?" .
Шашант

0

використовувати 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));
    }
}

0

Я написав бібліотеку для обробки андроїд перегляду рециркулятора перегляду події. Ви можете знайти весь підручник на 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;
            }
        });

Сер, я спробував вашу бібліотеку, але зіткнувся з проблемою з Maven?
Abhijeet

0

Анімація reciklierview не перевірена, інша нормальна. Я думаю, що це було максимально оптимізовано. Інтерфейс має інші види використання, які ви можете тимчасово ігнорувати.

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

BaseViewHolder як RecyclerView.ViewHolder
user8944115

R.id.tag_view_click потребує вас, щоб оголосити в каталозі значень <item type = "id" name = "tag_view_click" />
user8944115

-1

Приклад із getTag () / setTag () та слухачем одного клацання для всього адаптера:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    String[] mDataset = { "One", "Two", "Three" };

    private final View.OnClickListener mClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Object pos = v.getTag(R.id.TAG_POSITION);
            if(pos instanceof Integer) {
                // here is your position in the dataset that was clicked
                int position = (Integer) pos;

                ...
            }
        }
    };

    class MyViewHolder extends RecyclerView.ViewHolder {
        View mItemView;
        TextView mTextView; // as example
            ...

        MyViewHolder(View itemLayoutView){
            super(itemLayoutView);

            mItemView = itemLayoutView;
            mTextView = itemLayoutView.findViewById(R.id.my_text_view_id);
            ....
            itemLayoutView.setOnClickListener(mClickListener);
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override 
    public void onBindViewHolder(final ViewHolder holder, int position) {
        // you could use getTag() here for to get previous position 
        // that used the holder and for example cancel prev async requests 
        // to load images for the old position or so.
        //
        // setTag() new position that use the holder, so then user 
        // will click on the itemView - you will be able to get 
        // the position by getTag()
        holder.mItemView.setTag(R.id.TAG_POSITION, position);

        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        holder.mTextView.setText(mDataset[position]);
    }

    @Override
    public @NonNull MyAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View item_layout = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.my_recyclerview_item_layout, parent, false);
        ....
        return new MyViewHolder(item_layout);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}

TAG_POSITION визначений у файлі tags.xml як

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <item name="TAG_POSITION" type="id"/>
 </resources>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.