为什么RecyclerView没有onItemClickListener()? [关闭]

本文翻译自:Why doesn't RecyclerView have onItemClickListener()? [closed]

Closed . 已关闭 This question is opinion-based. 这个问题是基于意见的。 It is not currently accepting answers. 它当前不接受答案。 Learn more . 了解更多

Want to improve this question? 想改善这个问题吗? Update the question so it can be answered with facts and citations by editing this post . 更新问题,以便通过编辑此帖子以事实和引用的形式回答。

Closed 4 months ago . 4个月前关闭。

I was exploring RecyclerView and I was surprised to see that RecyclerView does not have onItemClickListener() . 我在浏览RecyclerView ,惊讶地发现RecyclerView没有onItemClickListener() Because RecyclerView extends 由于RecyclerView扩展

android.view.ViewGroup android.view.ViewGroup

and ListView extends ListView扩展

android.widget.AbsListView android.widget.AbsListView

. However I solved my problem by writing onClick in my RecyclerView.Adapter : 但是我通过在RecyclerView.Adapter编写onClick解决了我的问题:

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

    }
}

But still I want to know why Google removed onItemClickListener() ? 但是我仍然想知道为什么Google删除了onItemClickListener()

Is there a performance issue or something else? 是否存在性能问题或其他问题?


#1楼

参考:https://stackoom.com/question/1gPmZ/为什么RecyclerView没有onItemClickListener-关闭


#2楼

tl;dr 2016 Use RxJava and a PublishSubject to expose an Observable for the clicks. tl 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();
    }
}

Original Post: 原始帖子:

Since the introduction of ListView , onItemClickListener has been problematic. 自从引入ListView以来, onItemClickListener一直存在问题。 The moment you have a click listener for any of the internal elements the callback would not be triggered but it wasn't notified or well documented (if at all) so there was a lot of confusion and SO questions about it. 当您拥有用于任何内部元素的单击侦听器时,将不会触发该回调,但是该回调没有得到通知或没有得到充分的文档记录(如果有的话)(因此),因此存在很多混乱和疑问。

Given that RecyclerView takes it a step further and doesn't have a concept of a row/column, but rather an arbitrarily laid out amount of children, they have delegated the onClick to each one of them, or to programmer implementation. 鉴于RecyclerView更进一步,它没有行/列的概念,而是任意布局的子代,因此他们将onClick委托给了每个子代或程序员实现。

Think of Recyclerview not as a ListView 1:1 replacement but rather as a more flexible component for complex use cases. 不要将Recyclerview视为ListView 1:1的替代品,而Recyclerview视为用于复杂用例的更灵活的组件。 And as you say, your solution is what google expected of you. 正如您所说,您的解决方案就是Google对您的期望。 Now you have an adapter who can delegate onClick to an interface passed on the constructor, which is the correct pattern for both ListView and Recyclerview . 现在,您有了一个适配器,可以将onClick委托给在构造函数上传递的接口,这是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);
    }

}

and then on your adapter 然后在您的适配器上

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

Now look into that last piece of code: onCreateViewHolder(ViewGroup parent, int viewType) the signature already suggest different view types. 现在查看最后一段代码: onCreateViewHolder(ViewGroup parent, int viewType)签名已经建议了不同的视图类型。 For each one of them you'll require a different viewholder too, and subsequently each one of them can have a different set of clicks. 对于其中的每一个,您也将需要一个不同的视图持有者,随后,每个人都可以具有不同的点击集。 Or you can just create a generic viewholder that takes any view and one onClickListener and applies accordingly. 或者,您可以只创建一个通用的视图持有者,该视图持有者使用任何视图和一个onClickListener并相应地应用。 Or delegate up one level to the orchestrator so several fragments/activities have the same list with different click behaviour. 或将一个级别委派给协调器,以便多个片段/活动具有相同的列表且具有不同的单击行为。 Again, all flexibility is on your side. 同样,所有灵活性都在您身边。

It is a really needed component and fairly close to what our internal implementations and improvements to ListView were until now. 它是一个真正需要的组件,与我们迄今为止对ListView内部实现和改进非常接近。 It's good that Google finally acknowledges it. Google最终承认这一点是一件好事。


#3楼

> How RecyclerView is different from Listview? > RecyclerView与Listview有何不同?

One difference is that there is LayoutManager class with RecyclerView by which you can manage your RecyclerView like- 一个区别是,RecyclerView有一个LayoutManager类,您可以通过该类来管理RecyclerView ,如下所示:

Horizontal or Vertical scrolling by LinearLayoutManager 通过LinearLayoutManager 水平或垂直滚动

GridLayout by GridLayoutManager GridLayout通过GridLayoutManager

Staggered GridLayout by StaggeredGridLayoutManager 交错的网格布局由StaggeredGridLayoutManager

Like for horizontal scrolling for RecyclerView- 就像用于RecyclerView-的水平滚动一样

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

#4楼

I like this way and I'm using it 我喜欢这种方式,我正在使用它

Inside

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

Put

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

And create this class anywhere you want it 并在任何需要的地方创建此类

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

I've read before that there is a better way but I like this way is easy and not complicated. 我之前读过,有一种更好的方法,但是我喜欢这种方法简单而不复杂。


#5楼

How to put it all together example... 如何将所有内容放在一起示例...

  • onClick() handling onClick()处理
  • Cursor - RecyclerView 光标-RecyclerView
  • ViewHolder types 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); } } 

#6楼

Thanks to @marmor, I updated my answer. 感谢@marmor,我更新了答案。

I think it's a good solution to handle the onClick() in the ViewHolder class constructor and pass it to the parent class via OnItemClickListener interface. 我认为这是在ViewHolder类构造函数中处理onClick()并将其通过OnItemClickListener接口传递给父类的好方法

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

Usage of adapter in other classes: 适配器在其他类中的用法:

MyFragment.java 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
    }
}

}
发布了0 篇原创文章 · 获赞 73 · 访问量 55万+

猜你喜欢

转载自blog.csdn.net/w36680130/article/details/105325532