Tại sao RecyclerView không có onItemClickListener ()?


965

Tôi đã khám phá RecyclerViewvà tôi đã ngạc nhiên khi thấy điều RecyclerViewđó không có onItemClickListener().

Tôi có hai câu hỏi.

Câu hỏi chính

Tôi muốn biết lý do tại sao Google loại bỏ onItemClickListener()?

Có một vấn đề hiệu suất hoặc cái gì khác?

Câu hỏi phụ

Tôi đã giải quyết vấn đề của mình bằng cách viết onClickvào 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) {

    }
}

Đây có phải là ok / có cách nào tốt hơn?


31
Để mã của bạn hoạt động, bạn cần thêm itemLayoutView.setOnClickListener (cái này); trong hàm tạo ViewHolder.
whitneyland

3
bạn đã hỏi một câu hỏi hay ... nhiều nhà phát triển có cùng nghi ngờ về RecyclerView và ListView.
venkat

17
Câu hỏi: Tại sao RecyclerView không có OnItemClickListner ()? Tất cả các câu trả lời dưới đây ngoại trừ 2: Cách thêm onclick cho RecyclerView
Manohar Reddy

2
Chúc ai đó thực sự dành thời gian để trả lờiQUESTION
Ojonugwa Jude Ochalifu

1
Tôi nghĩ rằng họ đã loại bỏ nó vì vấn đề hiệu năng và bộ nhớ rác. Nhưng nhìn vào câu trả lời được chấp nhận .... nó sẽ tạo ra những vấn đề tương tự. Tôi nghĩ rằng bạn đã phạm sai lầm bằng cách chấp nhận nó và bây giờ các ppl khác mắc lỗi vì bạn :).
Alex

Câu trả lời:


1207

tl; dr 2016 Sử dụng RxJava và PublishSubject để hiển thị một Quan sát cho các nhấp chuột.

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

Bài gốc:

Kể từ khi giới thiệu ListView, onItemClickListenerđã có vấn đề. Khoảnh khắc bạn có một trình lắng nghe nhấp chuột cho bất kỳ yếu tố nội bộ nào, cuộc gọi lại sẽ không được kích hoạt nhưng nó không được thông báo hoặc ghi lại rõ ràng (nếu có), vì vậy có rất nhiều nhầm lẫn và câu hỏi SO về nó.

Cho rằng RecyclerViewnó tiến thêm một bước và không có khái niệm về một hàng / cột, mà là số lượng trẻ em được đặt ra một cách tùy tiện, họ đã ủy thác onClick cho mỗi người trong số họ hoặc để thực hiện lập trình viên.

Hãy nghĩ rằng Recyclerviewkhông phải là một ListViewthay thế 1: 1 mà là một thành phần linh hoạt hơn cho các trường hợp sử dụng phức tạp. Và như bạn nói, giải pháp của bạn là những gì google mong đợi ở bạn. Bây giờ bạn có một bộ điều hợp có thể ủy quyền cho onClick vào một giao diện được truyền trên hàm tạo, đây là mẫu chính xác cho cả hai ListViewRecyclerview.

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

}

và sau đó trên bộ chuyển đổi của bạn

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

Bây giờ hãy nhìn vào đoạn mã cuối cùng đó: onCreateViewHolder(ViewGroup parent, int viewType)chữ ký đã gợi ý các kiểu xem khác nhau. Đối với mỗi người trong số họ, bạn cũng sẽ yêu cầu một người xem khác nhau và sau đó mỗi người trong số họ có thể có một bộ nhấp chuột khác nhau. Hoặc bạn chỉ có thể tạo một khung nhìn chung có bất kỳ khung nhìn nào và một khung nhìn onClickListenervà áp dụng tương ứng. Hoặc ủy quyền một cấp cho người điều phối để một số phân đoạn / hoạt động có cùng danh sách với hành vi nhấp khác nhau. Một lần nữa, tất cả sự linh hoạt là về phía bạn.

Nó là một thành phần thực sự cần thiết và khá gần với những gì triển khai và cải tiến nội bộ của chúng tôi ListViewcho đến bây giờ. Thật tốt khi cuối cùng Google cũng thừa nhận điều đó.


78
RecyclerView.ViewHolder có phương thức getPocation (). Với một số điều chỉnh cho mã này, bạn có thể chuyển vị trí cho người nghe.
se_bastiaan

13
Anh ta hoàn toàn bỏ lỡ onBindViewHolder(holder, position), đó là nơi mà thông tin được cho là được điền vào.
MLProgrammer-CiM

26
@se_bastiaan getPosition()không được dùng nữa "Phương pháp này không được chấp nhận vì ý nghĩa của nó không rõ ràng do việc xử lý async * của các cập nhật bộ điều hợp. Vui lòng sử dụng {@link #getLayoutP vị trí ()} hoặc * {@link #getAd ModulePocation ()} tùy thuộc vào trường hợp sử dụng của bạn. "
upenpat 18/03/2015

8
Bạn có một vài lựa chọn. Đặt vị trí trong một trường int bên trong khung nhìn, cập nhật nó trong khi ràng buộc, sau đó trả lại khi nhấp là một.
MLProgrammer-CiM

8
@ MLProgrammer-CiM "Bạn có một số tùy chọn. Đặt vị trí trong trường int bên trong khung nhìn, cập nhật nó trong khi ràng buộc, sau đó trả lại khi nhấp là một." ViewHolder đã có giá trị này và đã được đặt. Chỉ cần gọiviewHolder.getPosition();
Chad Bingham

104

Tại sao RecyclerView không có onItemClickListener

Đây RecyclerViewlà một hộp công cụ, trái ngược với cái cũ, ListViewnó có ít tính năng xây dựng hơn và linh hoạt hơn. Đây onItemClickListenerkhông phải là tính năng duy nhất bị xóa khỏi ListView. Nhưng nó có rất nhiều người nghe và phương pháp để mở rộng nó theo ý thích của bạn, nó mạnh hơn rất nhiều trong tay phải;).

Theo tôi, tính năng phức tạp nhất bị loại bỏ RecyclerViewFast Scroll . Hầu hết các tính năng khác có thể dễ dàng thực hiện lại.

Nếu bạn muốn biết những tính năng thú vị khác RecyclerViewđược thêm vào, hãy đọc câu trả lời này cho câu hỏi khác.

Bộ nhớ hiệu quả - giải pháp thả vào cho onItemClickListener

Giải pháp này đã được đề xuất bởi Hugo Visser , một GDE Android, ngay sau khi RecyclerViewđược phát hành. Anh ấy đã tạo một lớp miễn phí có sẵn để bạn chỉ cần nhập mã của bạn và sử dụng nó.

Nó giới thiệu một số tính linh hoạt được giới thiệu bằng RecyclerViewcách sử dụng RecyclerView.OnChildAttachStateChangeListener.

Chỉnh sửa 2019 : phiên bản kotlin của tôi, java một, từ Hugo Visser, được giữ bên dưới

Kotlin / Java

Tạo một tập tin values/ids.xmlvà đặt nó trong đó:

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

sau đó thêm mã dưới đây vào nguồn của bạn

Kotlin

Sử dụng:

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

(nó cũng hỗ trợ nhấp vào mục dài và xem bên dưới để biết một tính năng khác mà tôi đã thêm).

triển khai (sự thích ứng của tôi với mã Java của 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) }
}

(xem bên dưới bạn cũng cần thêm một tệp XML)

Tính năng thưởng của phiên bản Kotlin

Đôi khi bạn không muốn tất cả các mục của RecyclerView có thể nhấp được.

Để xử lý việc này, tôi đã giới thiệu ItemClickSupportViewHoldergiao diện mà bạn có thể sử dụng ViewHolderđể kiểm soát mục nào có thể nhấp được.

Thí dụ:

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

Java

Sử dụng:

ItemClickSupport.addTo(mRecyclerView)
        .setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
    @Override
    public void onItemClicked(RecyclerView recyclerView, int position, View v) {
        // do it
    }
});

(nó cũng hỗ trợ nhấp vào mục dài)

Thực hiện (ý kiến ​​được thêm bởi tôi):

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

Cách thức hoạt động (tại sao nó hiệu quả)

Lớp này hoạt động bằng cách gắn một RecyclerView.OnChildAttachStateChangeListenerđến RecyclerView. Người nghe này được thông báo mỗi khi một đứa trẻ được gắn hoặc tách ra khỏi RecyclerView. Mã sử ​​dụng điều này để nối một người nghe nhấn / nhấp dài vào chế độ xem. Người nghe đó hỏi RecyclerViewcái RecyclerView.ViewHoldernào chứa vị trí.

Đây là giải pháp hiệu quả hơn các giải pháp khác vì nó tránh tạo nhiều người nghe cho mỗi chế độ xem và tiếp tục phá hủy và tạo chúng trong khi RecyclerViewđang được cuộn.

Bạn cũng có thể điều chỉnh mã để trả lại cho chủ sở hữu nếu bạn cần thêm.

Nhận xét cuối cùng

Hãy nhớ rằng HOÀN TOÀN tốt để xử lý nó trong bộ điều hợp của bạn bằng cách đặt trên mỗi chế độ xem danh sách của bạn một trình nghe nhấp chuột, giống như câu trả lời khác được đề xuất.

Đó không phải là điều hiệu quả nhất để làm (bạn tạo một trình nghe mới mỗi khi bạn sử dụng lại một khung nhìn) nhưng nó hoạt động và trong hầu hết các trường hợp, đó không phải là vấn đề.

Nó cũng là một chút chống lại sự phân tách mối quan tâm vì nó không thực sự là Công việc của Bộ điều hợp để ủy thác các sự kiện nhấp chuột.


addOnChildAttachStateChangeListener trong hàm tạo và không bao giờ xóa nó?
Saba

@Saba bạn không cần phải sử dụng hàm tạo trực tiếp mà thông qua các phương thức tĩnh addTo()removeFrom(). Ví dụ được liên kết với thẻ recyclerview và chết với nó hoặc khi nó được gỡ bỏ bằng tay.
Daniele Segato

Quá nhiều mã khi tất cả những gì bạn cần làm là đặt một trình nghe nhấp chuột vào onCreateViewHoldervà đó là mã đó.
EpicPandaForce

Đối số của bạn được truyền đi, không phải về số lượng mã mà là nguyên tắc hiệu quả và trách nhiệm và số lượng mã không phải là một dấu hiệu tuyệt vời cho giải pháp tốt / xấu, đặc biệt khi mã đó có thể tái sử dụng và hiệu quả. Một khi bạn đã đặt mã đó vào dự án của bạn (mà bạn có thể coi là bao gồm thư viện), tất cả những gì bạn cần là một lớp lót trên RecyclerView.
Daniele Segato

@DanieleSegato Cảm ơn bạn rất nhiều vì điều này!
Ông Octodood

90

Tôi thích cách này và tôi đang sử dụng nó

Phía trong

public Adapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)

Đặt

View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_image_and_text, parent, false);
v.setOnClickListener(new MyOnClickListener());

Và tạo lớp này bất cứ nơi nào bạn muốn

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

Tôi đã đọc trước đó rằng có một cách tốt hơn nhưng tôi thích cách này dễ dàng và không phức tạp.


23
có lẽ nên biến MyOnClickListenerthành cá thể, bởi vì bạn sẽ tạo một phiên bản mới của nó mọi lúc với newTừ khóa
user2968401

14
recyclerView.getChildP vị trí (v); bây giờ được sử dụng recyclerView. indexOfChild (v);
Qadir Hussain

4
Bạn không có vấn đề khi cuộn? Trong các thử nghiệm của tôi, itemPocation dường như phụ thuộc vào tái chế lượt xem, do đó, nó bị sai so với danh sách đầy đủ của tôi trong bộ điều hợp.
Simon

16
Làm cách nào để recyclerViewtruy cập MyOnClickListener?
guo

3
Thay vì sử dụng: int itemPocation = recyclerView.indexOfChild (v); bạn nên sử dụng: RecyclerView.ViewHolder chủ = recyclerView.getChildViewHolder (v); int itemP vị trí = chủ.getAd CHƯƠNGP vị trí ();
Gordon Freeman

35

Android Recyclerview Với onItemClickListener, Tại sao chúng ta không thể thử điều này chỉ hoạt động như thế ListView.

Nguồn: Liên kết

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

  }
}

Và đặt cái này thành 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
Không thanh lịch để thành thật.
vishal dharankar

@vishaldharankar tại sao không? Tôi nghĩ rằng đó là cách tốt nhất để tránh xung đột thay đổi tiêu điểm sau khi sử dụngSwipeRefreshLayout
NickUnuchek

Điều này là hoàn toàn không cần thiết, bạn có thể sử dụng View.OnClickListener thay vì chặn cảm ứng.
EpicPandaForce

23

Nhờ @marmor, tôi đã cập nhật câu trả lời của mình.

Tôi nghĩ rằng đó là một giải pháp tốt để xử lý onClick () trong hàm tạo của lớp ViewHolder và chuyển nó đến lớp cha thông qua giao diện OnItemClickListener .

MyAd CHƯƠNG.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());
    }
}
}

Sử dụng bộ chuyển đổi trong các lớp khác:

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
Điều này sẽ tạo và thu thập rác hàng tấn đối tượng khi cuộn, thay vào đó, bạn nên tạo một OnClickListener và sử dụng lại
marmor

2
Cũng onBindViewHolder()được gọi nhiều lần cho cùng một chế độ xem khi cuộn. Ngay cả việc tạo một đơn OnClickListenersẽ không giúp ích vì bạn sẽ đặt lại OnClickListenermỗi lần onBindViewHolder()được gọi. Thay vào đó, hãy xem giải pháp @ MLProgrammer-CiM.
vovahost

Bạn cũng có thể cải thiện giải pháp @ MLProgrammer-CiM và thay vì tạo một đối tượng người nghe cho mỗi Chế độ xem trong dòng này, MyAdapter.ViewHolder vh = new ViewHolder(v, new MyAdapter.ViewHolder.IMyViewHolderClicks() ....bạn có thể tạo một trình nghe duy nhất cho tất cả các chế độ xem.
vovahost

21

Các chàng trai sử dụng mã này trong hoạt động chính của bạn. Phương pháp rất hiệu quả

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

Đây là lớp Adaptor của bạn.

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

Sau này, bạn sẽ có được phương thức ghi đè này trong hoạt động của mình.

@Override
    public void OnCardClicked(View view, int position) {
        Log.d("OnClick", "Card Position" + position);
    }

5
Tôi tự hỏi nếu người dùng cuộn lên & xuống, OnClickListener sẽ tạo và tái chế nhiều lần, nó có hiệu quả không?
Vinh.TV

Để có được vị trí cập nhật của thẻ, điều nhỏ này có thể bị xâm phạm.
waqas ali

chào bạn, tôi rất thích hoạt động, tôi có một hoạt động yêu cầu với các danh mục và sản phẩm ban đầu, nếu người dùng chọn biểu tượng danh mục tôi cần hiển thị các danh mục trong hộp thoại hoặc hoạt động nếu người dùng chọn bất kỳ danh mục nào dựa trên dữ liệu được lọc vào hoạt động đầu tiên, vui lòng giúp tôi Cách giải quyết
Harsha

Thứ 1: Bạn cần thêm một phương thức có tên On SẢNtClicky trong giao diện OnCardClickListner giống như giao diện công cộng này OnCardClickListner {void OnCardClicky (Chế độ xem, vị trí int); void On SẢNtClicky (Chế độ xem, vị trí int); } Lần 2: Sau đó triển khai trình nghe nhấp vào biểu tượng sản phẩm của bạn như thế này. người giữ.your ProducttIcon.setOnClickListener (new View.OnClickListener () {@Override void công khai onClick (Xem v) {onCardClickListner.On SẢNtClicky (v, vị trí);}});
waqas ali

Tôi không thể ghi đè OnCardClicky. Làm thế nào mọi người nâng cao câu trả lời này?
The_Martian

19

> RecyclerView khác với Listview như thế nào?

Một điểm khác biệt là có LayoutManagerlớp với RecyclerView mà bạn có thể quản lý lượt thích của RecyclerViewmình-

Cuộn ngang hoặc dọc theoLinearLayoutManager

GridLayout bởi GridLayoutManager

GridLayout so le bởi StaggeredGridLayoutManager

Giống như cuộn ngang cho RecyclerView-

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

15

Theo dõi giải pháp RxJava tuyệt vời của MLProgrammer-CiM

Tiêu thụ / Quan sát lần nhấp

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. +

Sửa đổi bản gốc tl; dr là:

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

PublishSubject#asObservable()đã xóa bỏ. Chỉ cần trả lại PublishSubjectcái là một Observable.


Bạn đang nhận mClickConsumer ở ​​đâu trên dòng thứ hai, nếu bạn vui lòng giải thích một chút. rxAd Module.getPocationClicks (). đăng ký (mClickConsumer);
bluetoothfx

14

Làm thế nào để kết hợp tất cả lại với nhau ...

  • xử lý onClick ()
  • Con trỏ - RecyclerView
  • Các loại 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

Theo như tôi hiểu câu trả lời MLProgrammer-CiM , đơn giản là có thể làm điều này:

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

Sau khi đọc câu trả lời của @ MLProgrammer-CiM , đây là mã của tôi:

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

Coi chừng getAdapterPositioncó thể trở lại -1"không có vị trí".
EpicPandaForce

9

Tôi đã làm theo cách này, nó rất đơn giản:

Chỉ cần thêm 1 Line cho vị trí RecyclerView đã nhấp :

int position = getLayoutPosition()

Mã đầy đủ cho lớp 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()));
                }
            });
        }
    }

Hy vọng điều này sẽ giúp bạn.


Bạn nên sử dụng getAdapterPositionthay vì getLayoutPosition. Bảo vệ chống lại -1.
EpicPandaForce

7

Tôi sử dụng phương pháp này để bắt đầu một ý định từ 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);
       }
}
);

Đó không phải là cách tốt. Bởi vì onBindViewHolder(...)không được gọi theo notify()phương thức. Vì vậy, bạn có thể nhận được vị trí nhấp sai trong một số tình huống.
Alex Kisel

5

RecyclerView không có onItemClickListenervì RecyclerView chịu trách nhiệm tái chế các chế độ xem (bất ngờ!), Vì vậy, trách nhiệm của chế độ xem được tái chế để xử lý các sự kiện nhấp chuột mà nó nhận được.

Điều này thực sự làm cho nó dễ sử dụng hơn nhiều, đặc biệt nếu bạn có các mục có thể được nhấp vào nhiều nơi.


Dù sao, việc phát hiện nhấp chuột vào một mục RecyclerView là rất dễ dàng. Tất cả những gì bạn cần làm là xác định một giao diện (nếu bạn không sử dụng Kotlin, trong trường hợp đó bạn chỉ cần chuyển qua lambda):

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

Cùng mã trong Kotlin:

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

Điều bạn không cần phải làm:

1.) bạn không cần chặn các sự kiện chạm bằng tay

2.) bạn không cần phải loay hoay với những người lắng nghe thay đổi trạng thái

3.) bạn không cần PublishSubject / PublishRelay từ RxJava

Chỉ cần sử dụng một người nghe nhấp chuột.


4

Xem cách tiếp cận của tôi về điều này:

Đầu tiên khai báo một giao diện như thế này:

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

Sau đó tạo bộ điều hợp:

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

Và bây giờ chúng ta hãy xem làm thế nào để tích hợp điều này từ một mảnh:

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

Nếu bạn muốn thêm onClick () vào chế độ xem con của các mục, ví dụ: một nút trong mục, tôi thấy rằng bạn có thể thực hiện dễ dàng trong onCreateViewHolder () của RecyclerView.Ad CHƯƠNG của riêng bạn như thế này:

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

Tôi không biết liệu đó có phải là một cách tốt hay không, nhưng nó hoạt động tốt. Nếu bất cứ ai có một ý tưởng tốt hơn, rất vui mừng nói với tôi và sửa câu trả lời của tôi! :)


4

Đây là một cách để thực hiện nó khá dễ dàng nếu bạn có một danh sách POJO và muốn lấy một lần nhấp từ bên ngoài bộ chuyển đổi.

Trong bộ điều hợp của bạn, hãy tạo một trình lắng nghe cho các sự kiện nhấp chuột và một phương thức để đặt nó:

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

}

Trong ViewHolder của bạn, triển khai onClickListener và tạo một thành viên lớp để tạm thời lưu trữ POJO mà chế độ xem đang trình bày, theo cách đó (đây là một ví dụ, tạo setter sẽ tốt hơn):

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

Quay lại bộ điều hợp của bạn, đặt POJO hiện tại khi ViewHolder bị ràng buộc (hoặc thành null nếu chế độ xem hiện tại không có):

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

Đó là nó, bây giờ bạn có thể sử dụng nó như thế này từ mảnh / hoạt động của bạn:

    mMyAdapter.setOnItemClickListener(new mMyAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(MyPojo pojo) {
            //Do whatever you want with your pojo here
        }
    });

bạn đang cố gắng thực hiện Giao diện như thể nó sẽ là một phương thức Object. Điều này sẽ không hoạt động
Akshay

Cập nhật: Những gì bạn cần làmMyAdapter.setOnItemClickListener(MyAdapter.OnItemClickListener() {});
Akshay

@Akshay Xin lỗi nhưng tôi nghĩ mã của tôi là chính xác. Bạn đã thử à? Tất cả những người nghe trên Android đều tuân theo cùng một nguyên tắc và bạn cần khởi tạo giao diện bằng một 'mới' (ví dụ: stackoverflow.com/questions/4709870/ Lỗi )
Simon

Người nghe không nên tĩnh.
EpicPandaForce

4

Vâng bạn có thể

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

Tại đây bạn có thể xử lý nhiều onclick xem mã bên dưới và nó rất hiệu quả

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


}

Ông có nghĩa là itemView.
Davideas

3

Sửa đổi nhận xét của tôi ...

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
Đó là một gợi ý, nhưng tốt hơn nếu bạn xóa câu trả lời này, trừ khi nhiều người sẽ bỏ phiếu tiêu cực. Giải pháp này chắc chắn sai, nó hoạt động nhưng lập trình xấu. Vui lòng đọc một số blog cho RecyclerView và nghiên cứu một số thư viện Adaptor, đã có nhiều hợp lệ trong Github, như Linh hoạt Chương trình, FastAd CHƯƠNG, v.v ...
Davideas

1
Bạn trả lời tương đối mới với một danh sách dài các câu trả lời được bình chọn cao và ở cuối trang, đó là lý do tại sao nó vẫn có 0 phiếu. Nhân tiện, trong mã mới, bạn nên sử dụng getAdapterPosition().
Davideas

Bạn chỉ cần đọc javaDoc của 2 phương thức.
Davideas

3

Kiểm tra cái này trong đó tôi đã thực hiện tất cả mọi thứ một cách thích hợp

Lớp tái chế

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

}

Bộ chuyển đổi

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

Giao diện

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

Điều này làm việc cho tôi:

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

Nó không phải là thiết lập cách tốt setOnClickListener(holder)trong onBindViewHolder(). Bởi vì onBindViewHolder (...) không được gọi sau các phương thức notify (). Vì vậy, bạn có thể nhận được vị trí nhấp sai trong một số tình huống.
Alex Kisel

Bạn nên tạo trình nghe nhấp chuột bên trong onCreateViewHolder.
EpicPandaForce

2

Truy cập phần mainViewcủa rowLayout(cell)bạn RecyclerViewOnBindViewHolderviết mã này:

    @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

Thay vì triển khai giao diện View.OnClickListener bên trong chủ sở hữu chế độ xem hoặc tạo và giao diện và giao diện triển khai trong hoạt động của bạn .. Tôi đã sử dụng mã này cho đơn giản khi triển khai 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
Và bạn nghĩ rằng việc tạo một đối tượng OnItemClickListener mới tại mỗi lệnh gọi phương thức onBind () là một cách tiếp cận tốt?
Rạp xiếc Alexandru

Làm tốt lắm. Hoạt động tuyệt vời!
sam

1

nó làm việc cho tôi Hy vọng nó sẽ giúp. Cách đơn giản nhất.

Người xem bên trong

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

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

Bên trong 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();
                }
            });

Và cho tôi biết, bạn có câu hỏi nào về giải pháp này không?


Bạn nên đặt onClickListener onCreateViewHolderđể có hiệu suất tốt hơn, nhưng sau đó bạn cần có được vị trí bằng cách sử dụng getAdapterPosition()(và bảo vệ chống lại -1)
EpicPandaForce

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

        }
    });

sử dụng u này có thể nhận được kết quả chính xác ..................
dileep krishnan

Câu hỏi là "Tại sao RecyclerView không có onItemClickListener ()?" KHÔNG "Cách triển khai onItemClickListener () trong RecyclerView?" .
Shashanth

0

sử dụng 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

Tôi đã viết một thư viện để xử lý sự kiện nhấp vào mục tái chế android. Bạn có thể tìm thấy toàn bộ hướng dẫn trong 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
            }
        });

hoặc để xử lý mục nhấn lâu bạn có thể sử dụng

RecycleClick.addTo(YOUR_RECYCLEVIEW).setOnItemLongClickListener(new RecycleClick.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
                // YOUR CODE
                return true;
            }
        });

Thưa ông, tôi đã thử thư viện của bạn nhưng phải đối mặt với vấn đề với maven?
Abhijeet

0

hoạt hình tái chế chưa được thử nghiệm, khác là bình thường. Tôi nghĩ rằng nó đã được tối ưu hóa đến mức tối đa. Giao diện có công dụng khác, bạn có thể tạm thời bỏ qua.

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

}

Đây là giao diện

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

Đây là một lớp trừu tượng

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

Bạn chỉ cần nó

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

        }

        @Override
        public boolean onItemLongClick(View view, Integer integer) {
            return true;
        }
    });

BaseViewHolder dưới dạng RecyclerView.ViewHolder
user8944115

R.id.tag_view_click cần bạn khai báo trong thư mục giá trị <item type = "id" name = "tag_view_click" />
user8944115

-1

Ví dụ với getTag () / setTag () và trình nghe nhấp chuột đơn cho toàn bộ bộ điều hợp:

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 được định nghĩa trong tệp tags.xml là

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <item name="TAG_POSITION" type="id"/>
 </resources>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.