【问题标题】:Get clicked item and its position in RecyclerView获取点击的项目及其在 RecyclerView 中的位置
【发布时间】:2015-04-02 12:26:59
【问题描述】:

我正在用RecyclerView 替换我的ListView,列表显示正常,但我想知道如何获取点击的项目及其位置,类似于我们在ListView 中使用的方法OnItemClickListener.onItemClick(AdapterView parent, View v, int position, long id)

感谢您的想法!

【问题讨论】:

标签: android listview android-recyclerview


【解决方案1】:

基于链接:Why doesn't RecyclerView have onItemClickListener()? and How RecyclerView is different from Listview?,以及@Duncan的大致思路,我在这里给出我的解决方案:

  • 定义一个接口RecyclerViewClickListener,用于从适配器向Activity/Fragment传递消息:

      public interface RecyclerViewClickListener {
          public void recyclerViewListClicked(View v, int position);
      }
    
  • Activity/Fragment中实现接口,并将监听器传递给适配器:

      @Override
      public void recyclerViewListClicked(View v, int position){... ...}
    
      //set up adapter and pass clicked listener this
      myAdapter = new MyRecyclerViewAdapter(context, this);
    
  • AdapterViewHolder

      public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ItemViewHolder> {
         ... ... 
         private Context context;
         private static RecyclerViewClickListener itemListener;
    
    
         public MyRecyclerViewAdapter(Context context, RecyclerViewClickListener itemListener) {
             this.context = context;
             this.itemListener = itemListener;
             ... ...
         }
    
    
         //ViewHolder class implement OnClickListener, 
         //set clicklistener to itemView and, 
         //send message back to Activity/Fragment 
         public static class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
             ... ...
             public ItemViewHolder(View convertView) {
                 super(convertView);
                 ... ...
                 convertView.setOnClickListener(this);
             }
    
             @Override
             public void onClick(View v) {
                 itemListener.recyclerViewListClicked(v, this.getPosition());     
    
             }
         }
      }
    

经过测试,一切正常。

[更新]

自 API 22 起,RecyclerView.ViewHolder.getPosition() 已弃用,因此改为使用 getLayoutPosition()

【讨论】:

  • 使用 getLayoutPosition() 方法,因为 getPosition() 现已弃用。
  • 感谢这段代码 :) 只是一个提示:在构造函数中以静态方式引用静态 private static RecyclerViewClickListener itemListener;
  • @khurramengr 我最终在onBindViewHolder 方法中的每个项目中添加了一个侦听器。此外,我的列表项并非全部可点击(仅部分项)。
  • 你不应该在这里使用弱引用吗:new MyRecyclerViewAdapter(context, this)?这可能会导致内存泄漏
  • public void recyclerViewListClicked(View v, int position); 修饰符public 对于接口方法是多余的
【解决方案2】:
public class MyRvAdapter extends RecyclerView.Adapter<MyRvAdapter.MyViewHolder>{
    public Context context;
    public ArrayList<RvDataItem> dataItems;

    ...
    constructor
    overrides
    ...

    class MyViewHolder extends RecyclerView.ViewHolder{
        public TextView textView;
        public Context context;

        public MyViewHolder(View itemView, Context context) {
            super(itemView);

            this.context = context;

            this.textView = (TextView)itemView.findViewById(R.id.textView);

            // on item click
            itemView.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    // get position
                    int pos = getAdapterPosition();

                    // check if item still exists
                    if(pos != RecyclerView.NO_POSITION){
                        RvDataItem clickedDataItem = dataItems.get(pos);
                        Toast.makeText(v.getContext(), "You clicked " + clickedDataItem.getName(), Toast.LENGTH_SHORT).show();
                    }
                }
            });
        }
    }
}

【讨论】:

  • 谢谢。 getAdapterPosition() 是我需要的。
  • pos != RecyclerView.NO_POSITION 可能会失败。例如:位置 = -2。 pos > RecyclerView.NO_POSITION 更好..我认为?
【解决方案3】:

这是一个设置点击监听器的示例。

Adapter extends  RecyclerView.Adapter<MessageAdapter.MessageViewHolder> {  ...  }

 public static class MessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public TextView     tv_msg;
        public TextView     tv_date;
        public TextView     tv_sendTime;
        public ImageView    sharedFile;
        public ProgressBar sendingProgressBar;

        public MessageViewHolder(View v) {
            super(v);
            tv_msg =  (TextView) v.findViewById(R.id.tv_msg);
            tv_date = (TextView)  v.findViewById(R.id.tv_date);
            tv_sendTime = (TextView)  v.findViewById(R.id.tv_sendTime);
            sendingProgressBar = (ProgressBar) v.findViewById(R.id.sendingProgressBar);
            sharedFile = (ImageView) v.findViewById(R.id.sharedFile);

            sharedFile.setOnClickListener(this);

        }

        @Override
        public void onClick(View view) {
        int position  =   getAdapterPosition();


            switch (view.getId()){
                case R.id.sharedFile:


                    Log.w("", "Selected"+position);


                    break;
            }
        }

    }

【讨论】:

  • 什么是getAdapterPostion();在这里?
  • 点击视图的位置。不是自定义方法。
  • 这似乎是最简洁的方法,尤其是因为您几乎总是对使用位置获取数据感兴趣并且可能无法访问 RecyclerView 对象。
  • 非常感谢int position = getAdapterPosition();
  • 请注意,在更新列表时调用 getAdapterPosition,例如因为调用了notififyXYZ,将返回-1
【解决方案4】:

将此代码放在您在活动中定义回收站视图的位置。

    rv_list.addOnItemTouchListener(
            new RecyclerItemClickListener(activity, new RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View v, int position) {

                    Toast.makeText(activity, "" + position, Toast.LENGTH_SHORT).show();
                }
            })
    );

然后做一个单独的类并放上这段代码:

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
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) {

    }
}

【讨论】:

    【解决方案5】:

    用下面的代码创建java文件

    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.getChildLayoutPosition(childView));
            return true;
        }
        return false;
    }
    
    @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
    
    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    
    }
    

    只需在你的 RecyclerView 对象上使用监听器。

    recyclerView.addOnItemTouchListener(  
    new RecyclerItemClickListener(context, new RecyclerItemClickListener.OnItemClickListener() {
      @Override public void onItemClick(View view, int position) {
        // TODO Handle item click
      }
    }));
    

    【讨论】:

    • RecyclerItemClickListener 不是默认的 android 类,您应该提供您正在使用的库或类本身。
    【解决方案6】:

    如果你想点击活动/片段而不是适配器的回收视图事件,那么你也可以使用以下快捷方式。

    recyclerView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    final TextView txtStatusChange = (TextView)v.findViewById(R.id.txt_key_status);
                    txtStatusChange.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Log.e(TAG, "hello text " + txtStatusChange.getText().toString() + " TAG " + txtStatusChange.getTag().toString());
                            Util.showToast(CampaignLiveActivity.this,"hello");
                        }
                    });
                    return false;
                }
            });
    

    你也可以使用其他长方法,比如使用接口

    【讨论】:

    • ,,你好,但是如果我们也必须获得点击项目的位置怎么办。 ?
    • 可以通过adapter设置item在TAG中的位置,然后获取上面提到的tag txtStatusChange.getTag().toString()
    • @AmandeepRohila 为了使代码正常工作,我必须先滚动到水平回收视图的末尾。只有这样,吐司才起作用。有没有办法让它工作而不必先来回滚动?
    【解决方案7】:
    recyclerViewObject.addOnItemTouchListener(
        new RecyclerItemClickListener(
            getContext(),
            recyclerViewObject,
            new RecyclerItemClickListener.OnItemClickListener() {
                @Override public void onItemClick(View view, int position) {
                    // view is the clicked view (the one you wanted
                    // position is its position in the adapter
                }
                @Override public void onLongItemClick(View view, int position) {
                }
            }
        )
    );
    

    【讨论】:

    • 这不起作用,因为 RecyclerItemClickListener 不存在(不再存在)。
    【解决方案8】:

    使用下面的代码:-

    public class SergejAdapter extends RecyclerView.Adapter<SergejAdapter.MyViewHolder>{
    
    ...
    
    class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    
    
        @Override
        public void onClick(View v) {
            // here you use position
            int position = getAdapterPosition();
            ...
    
        }
    }
    }
    

    【讨论】:

      【解决方案9】:

      这里是找到被点击项目位置的最简单最简单的方法:

      我也遇到了同样的问题。

      我想找到 RecyclerView() 的点击/选择项目的位置,并对该特定项目执行一些特定的操作。

      getAdapterPosition() 方法对这类东西很有用。经过一天的长期研究并尝试了许多其他方法后,我发现了这种方法。

      int position = getAdapterPosition();
      Toast.makeText(this, "Position is: "+position, Toast.LENGTH_SHORT).show();
      

      您不必使用任何额外的方法。只需创建一个名为“位置”的全局变量,并在适配器的任何主要方法(类或类似方法)中使用 getAdapterPosition() 对其进行初始化。

      这是来自 link 的简短文档。

      getAdapterPosition 在 22.1.0 版本中添加 int getAdapterPosition () 返回此 ViewHolder 表示的项目的适配器位置。 请注意,如果有挂起的适配器更新但尚未发生新的布局传递,这可能与 getLayoutPosition() 不同。 在下一次布局遍历之前,RecyclerView 不会处理任何适配器更新。这可能会在用户在屏幕上看到的内容与适配器内容之间产生暂时的不一致。这种不一致并不重要,因为它会小于 16 毫秒,但如果您想使用 ViewHolder 位置来访问适配器,这可能是个问题。有时,您可能需要获取准确的适配器位置来执行某些操作以响应用户事件。在这种情况下,您应该使用这种方法来计算 ViewHolder 的 Adapter 位置。

      乐于助人。如有疑问,请随时提出。

      【讨论】:

      • 获取 RecyclerView 的点击项的价值的最简单和最简单的方法是什么?
      【解决方案10】:

      我的简单解决方案

      Make a position holder:
      
          public class PositionHolder {
      
          private int position;
      
          public PositionHolder(int position) {
              this.position = position;
          }
      
          public int getPosition() {
              return position;
          }
      
          public void setPosition(int position) {
              this.position = position;
          }
      }
      

      只需定位或放置您需要从活动中获取的数据。

      适配器构造函数:

      public ItemsAdapter(Context context, List<Item> items, PositionHolder positionHolder){
              this.context = context;
              this.items = items;
              this.positionHolder = positionHolder;
      }
      

      活动中:

       @Override
          protected void onCreate(Bundle savedInstanceState) {
      
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              selectedPosition = 0;
      
              positionHolder = new PositionHolder(selectedPosition);
              initView();
          }
      

      在 Adapter onClickLictener 项中
      在 onBindViewHolder

      holder.holderButton.setOnClickListener(v -> {
                  positionHolder.setPosition(position);
                  notifyDataSetChanged();
              });
      

      现在,每当您在 RecyclerView 中更改位置时,它都会保留在 Holder 中(或者它应该被称为 Listener)

      希望对你有用

      我的第一篇文章;P

      【讨论】:

        【解决方案11】:

        RecyclerView 不提供这种方法。

        为了管理 RecyclerView 上的点击事件,我最终在我的适配器中实现了 onClickListener,当绑定 ViewHolder 时:并且在绑定 viewHolder 时,我在其上设置了一个标签,其中包含我需要处理点击(例如位置)和 clicklistener 的信息

        【讨论】:

        • 是的,我们可以在 ViewHolder 中设置点击监听器,但我需要将点击的项目视图和位置传回我的片段以进行进一步处理
        【解决方案12】:

        每次我使用另一种方法。人们似乎在视图上存储或获取位置,而不是存储对 ViewHolder 显示的对象的引用。

        我改用这种方法,只在调用 onBindViewHolder() 时将其存储在 ViewHolder 中,并在 onViewRecycled() 中将引用设置为 null。

        每次 ViewHolder 变得不可见时,它都会被回收。所以这不会影响大量内存消耗。

        @Override
        public void onBindViewHolder(final ItemViewHolder holder, int position) {
            ...
            holder.displayedItem = adapterItemsList.get(i);
            ...
        }
        
        @Override
        public void onViewRecycled(ItemViewHolder holder) {
            ...
            holder.displayedItem = null;
            ...
        }
        
        class ItemViewHolder extends RecyclerView.ViewHolder {
            ...
            MySuperItemObject displayedItem = null;
            ...
        }
        

        【讨论】:

          【解决方案13】:

          在设计器中,您可以将 listItem 的 onClick 属性设置为使用单个参数定义的方法。我在下面定义了一个示例方法。 getAdapterPosition 方法将为您提供所选列表项的索引。

          public void exampleOnClickMethod(View view){
              myRecyclerView.getChildViewHolder(view).getAdapterPosition());
          }
          

          有关设置 RecyclerView 的信息,请参阅此处的文档:https://developer.android.com/guide/topics/ui/layout/recyclerview

          【讨论】:

            【解决方案14】:

            简单(但不那么明显)的解决方案是这样做:

            @Override
                public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
            
                    View v = LayoutInflater.from(viewGroup.getContext())
                            .inflate(R.layout.recycler_row, viewGroup, false);
                    ViewHolder vh = new ViewHolder(v);
            

            然后,无论何时,调用方法currentPosition = vh.getLayoutPosition();

            在我的例子中,我在那个 vh 视图上的 onClick 侦听器中执行此操作。恕我直言,recycleView 类错过了 .getPosition() 和我们从 ListView 知道的其他功能,有时是强制性的,根本不可用。我非常后悔从 ListView 转移到 Recycle 同上。时间窃贼花费了一天多的时间来揭开它的神秘面纱。糟糕的工程。 (但有什么可以的)

            【讨论】:

            • 在浪费了一整天寻找解决方案之后,我偶然发现了这正是我所需要的!完美运行。
            【解决方案15】:
            //Create below methods into the Activity which contains RecyclerView.
            
            
            private void createRecyclerView() {
            
            final RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
                recyclerView.setLayoutManager(new LinearLayoutManager(this));
                MyAdapter myAdapter=new MyAdapter(dataAray,MianActivity.this);
                recyclerView.setAdapter(myAdapter);
                recyclerView.setItemAnimator(new DefaultItemAnimator());
            
                setRecyclerViewClickListner(recyclerView);
            }
            
            private void setRecyclerViewClickListner(RecyclerView recyclerView){
            
                final GestureDetector gestureDetector = new GestureDetector(MainActivity.this,new GestureDetector.SimpleOnGestureListener() {
                    @Override public boolean onSingleTapUp(MotionEvent e) {
                        return true;
                    }
                });
            
            
                recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
                    @Override
                    public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
                        View child =recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
                        if(child!=null && gestureDetector.onTouchEvent(motionEvent)){
                           int position=recyclerView.getChildLayoutPosition(child);
                            String name=itemArray.get(position).name;
            
                        return true;
                    }
            
                    @Override
                    public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
            
                    }
            
                    @Override
                    public void onRequestDisallowInterceptTouchEvent(boolean b) {
            
                    }
                });
            }
            

            【讨论】:

            • 请添加解释,只是在没有介绍的情况下转储代码,以及如何解决问题的解释不是很有帮助。
            【解决方案16】:

            这样试试

            适配器类:

             public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {
            
            public interface OnItemClickListener {
                void onItemClick(ContentItem item);
            }
            
            private final List<ContentItem> items;
            private final OnItemClickListener listener;
            
            public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
                this.items = items;
                this.listener = listener;
            }
            
            @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
                return new ViewHolder(v);
            }
            
            @Override public void onBindViewHolder(ViewHolder holder, int position) {
                holder.bind(items.get(position), listener);
            }
            
            @Override public int getItemCount() {
                return items.size();
            }
            
            static class ViewHolder extends RecyclerView.ViewHolder {
            
                private TextView name;
                private ImageView image;
            
                public ViewHolder(View itemView) {
                    super(itemView);
                    name = (TextView) itemView.findViewById(R.id.name);
                    image = (ImageView) itemView.findViewById(R.id.image);
                }
            
                public void bind(final ContentItem item, final OnItemClickListener listener) {
                    name.setText(item.name);
                    itemView.setOnClickListener(new View.OnClickListener() {
                        @Override public void onClick(View v) {
                            listener.onItemClick(item);
                        }
                    });
                }
            }
            

            }

            在您的活动或片段中:

            ContentAdapter adapter = new ContentAdapter(itemList, this);
            

            注意:根据您在活动或片段和覆盖方法中给出的上下文实现 OnItemClickListener。

            【讨论】:

            • 上面的解决方案很好。您能否在上面添加内容以显示 Activity 中的 onItemClick 方法应该是什么样子?我正在尝试将项目的位置传递给 Intent(以打开下一个 Activity),但我一直得到错误的位置。
            【解决方案17】:

            Kotlin的短扩展
            方法返回所有项目的绝对位置(不是仅可见项目的位置)。

            fun RecyclerView.getChildPositionAt(x: Float, y: Float): Int {
                return getChildAdapterPosition(findChildViewUnder(x, y))
            }
            

            及用法

            val position = recyclerView.getChildPositionAt(event.x, event.y)
            

            【讨论】:

              【解决方案18】:

              在您的自定义接口 java 方法中使用 getLayoutPosition()。这将返回项目的选定位置,请在

              上查看完整详细信息

              https://becody.com/get-clicked-item-and-its-position-in-recyclerview/

              【讨论】:

              • 欢迎来到 Stack Overflow!虽然链接是分享知识的好方法,但如果它们在未来被破坏,它们将无法真正回答问题。将回答问题的链接的基本内容添加到您的答案中。如果内容太复杂或太大而无法在此处放置,请描述所提出解决方案的总体思路。请记住始终保留对原始解决方案网站的链接引用。见:How do I write a good answer?
              【解决方案19】:
              //simply check if the adapter position you get not less than zero
              
              holder.btnDelItem.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      if(holder.getAdapterPosition()>=0){
                          list.remove(holder.getAdapterPosition());
                          notifyDataSetChanged();
                      }
                  }
              });
              

              【讨论】:

              • 在回答一个老问题时,如果您包含一些上下文来解释您的答案如何提供帮助,那么您的答案将对其他 StackOverflow 用户更有用,特别是对于已经有一个已接受答案的问题。请参阅:How do I write a good answer
              【解决方案20】:

              经过大量试验和错误后,我发现有一种非常简单的方法可以做到这一点,即在适配器类中创建一个接口并在片段中实现它现在出现了转折,我在覆盖函数中实例化了视图模型现在在片段中,您可以将数据从该函数发送到 viemodel 并从那里发送到任何地方。

              我不知道这种方法是否是好的编码方法,但请在评论中告诉我,如果有人想在评论部分看到让我知道,我会定期打开 stackover 流程​​。

              【讨论】:

              • 听起来不错,但请您提供更多详细信息
              【解决方案21】:
              recyclerView.addOnItemTouchListener(object : AdapterView.OnItemClickListener,
                              RecyclerView.OnItemTouchListener {
                              override fun onItemClick(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                                  TODO("Not yet implemented")
                              }
              
                              override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
                                  TODO("Not yet implemented")
                              }
              
                              override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
                                  TODO("Not yet implemented")
                              }
              
                              override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
                                  TODO("Not yet implemented")
                              }
              
                          })
              

              如果使用 Kotlin

              【讨论】:

              • 虽然此代码可能会解决问题,including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的回答添加解释并说明适用的限制和假设。
              【解决方案22】:

              在 onViewHolder 中将 onClickListiner 设置为任何视图并在侧边单击中使用此代码:

              Toast.makeText(Drawer_bar.this, "position" + position, Toast.LENGTH_SHORT).show();

              Drawer_Bar 替换为您的活动名称。

              【讨论】:

                猜你喜欢
                • 2022-01-02
                • 1970-01-01
                • 2023-04-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多