获取被点击的item及其在RecyclerView中的位置

Get clicked item and its position in RecyclerView

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

中使用

谢谢你的想法!

RecyclerView 没有提供这样的方法

为了管理 RecyclerView 上的点击事件,我最终在绑定 ViewHolder 时在我的适配器中实现了 onClickListener:在我的 ViewHolder 中,我保留了对根视图的引用(就像您可以对 ImageView、TextView 等所做的那样)。 .) 并在绑定 viewHolder 时,我在其上设置了一个标签,其中包含我需要处理点击(例如位置)和点击监听器的信息

基于link: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实现接口,同时也将listener传递给adapter:

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

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

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

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

        }
    });
}

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

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

您还可以使用其他长方法,例如使用接口

将此代码放在您在 activity 中定义回收器视图的位置。

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

然后单独制作 class 并放入此代码:

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

    }
}

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

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

Drawer_Bar 替换为您的 Activity 姓名。

使用下面的代码:-

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

    }
}
}

这样试试

适配器class:

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

}

在您的 Activity 或片段中:

ContentAdapter adapter = new ContentAdapter(itemList, this);

注意:根据您在 activity 或片段和覆盖方法中提供的上下文实现 OnItemClickListener。

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

每次我使用另一种方法。人们似乎存储或获取视图上的位置,而不是存储对 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;
    ...
}

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

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

和用法

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

Here is the simplest and the easiest way to find the position of the clicked item:

我也遇到了同样的问题。

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

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

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

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

这是 link 的简短文档。

getAdapterPosition added in version 22.1.0 int getAdapterPosition () Returns the Adapter position of the item represented by this ViewHolder. Note that this might be different than the getLayoutPosition() if there are pending adapter updates but a new layout pass has not happened yet. RecyclerView does not handle any adapter updates until the next layout traversal. This may create temporary inconsistencies between what user sees on the screen and what adapter contents have. This inconsistency is not important since it will be less than 16ms but it might be a problem if you want to use ViewHolder position to access the adapter. Sometimes, you may need to get the exact adapter position to do some actions in response to user events. In that case, you should use this method which will calculate the Adapter position of the ViewHolder.

乐于助人。有疑问欢迎提问

在自定义界面 java 方法中使用 getLayoutPosition()。这将 return 项目的选定位置,请查看

上的完整详细信息

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

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

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

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

使用以下代码创建 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
  }
}));
//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();
        }
    }
});

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

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

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

我的简单解决方案

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

只需定位或放置您需要从 activity 获得的数据。

适配器构造函数:

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

在Activity中:

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

希望有用

我的第一个post;P

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

@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 class 遗漏了 .getPosition() 和我们从 ListView 知道的其他功能,有时是强制性的,根本不可用。我非常后悔从 ListView 转移到 Recycle 同上。时间窃贼花了一天多的时间来揭开它的神秘面纱。糟糕的工程。 (不过有什么也可以)