Get clicked item and its position in RecyclerView

I am replacing my ListView with RecyclerView, list showing ok, but I would like to know how to get clicked item and its position, similar to the method OnItemClickListener.onItemClick(AdapterView parent, View v, int position, long id) we use in ListView.

Thanks for ideas!

284138 次浏览

回收视图没有提供这样的方法。

To manage click events on RecyclerView I ended up implementing onClickListener in my adapter, when binding the ViewHolder: In my ViewHolder I keep a reference to the root view (as you can do with your ImageViews, TextViews, etc...) and when binding the viewHolder I set a tag on it with information I need to handle click (such as position) and a clicklistener

基于这个链接: 为什么回收视图没有 onitemclickListener () ? 回收视图和 Listview 有什么不同?,以及@Duncan 的总体想法,我在这里给出我的解决方案:

  • 为从适配器传递到 Activity/Fragment的消息定义一个接口 RecyclerViewClickListener:

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

下面是一个设置 Click 监听器的示例。

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


}
});
}

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

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

您还可以使用其他长的方式,如使用接口

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

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


}
}

在 onViewHolder 中,将 onClickListiner 设置为任意视图,并在侧面单击使用下面的代码:

Make Text (Drawer _ bar. this,“ position”+ position,Toast.LENGTH _ SHORT) . show () ;

用活动名称替换 Drawer_Bar

使用以下代码:-

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


}
}
}

这样试试

适配器类别:

 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 并覆盖方法。

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 显示的对象的引用。

I use this approach instead, and just store it in ViewHolder when onBindViewHolder() is called, and set reference to null in onViewRecycled().

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

科特林的简短分机
方法返回所有项的绝对位置(而不是仅可见项的位置)。

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

还有用法

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

下面是找到被点击项目位置的最简单和最容易的方法:

我也遇到过同样的问题。

我希望找到回收视图()的单击/选择项的位置,并对该特定项执行一些特定操作。

getAdapterPosition() method works like a charm for these kind of stuff. I found this method after a day of long research and after trying numerous other methods.

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

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

这里是一个简短的文档从这个 链接

GetAdapterposition 在22.1.0版本中添加 Int getAdapterposition () 返回此 ViewHolder 表示的项的 Adapter 位置。 注意,如果有挂起的适配器更新,但是还没有发生新的布局传递,那么这可能不同于 getLayoutposition ()。 回收视图在下一次布局遍历之前不处理任何适配器更新。这可能会在用户在屏幕上看到的内容和适配器内容之间产生临时的不一致。这种不一致性并不重要,因为它将小于16ms,但是如果您想使用 ViewHolder 位置访问适配器,这可能是一个问题。有时,您可能需要获得准确的适配器位置来响应用户事件执行某些操作。在这种情况下,您应该使用这个方法来计算 ViewHolder 的 Adapter 位置。

乐意效劳,有什么疑问尽管提。

在自定义接口 java 方法中使用 getLayoutposition ()。这将返回一个项目的选定位置,检查完整的详细信息

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

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

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

有关设置回收视图的信息,请参阅这里的文档: 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.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();
}
}
});

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

我不知道这个方法是否是好的编码方法,但请让我知道在评论,如果有人想看到让我知道在评论部分,我定期打开堆叠流。

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


})

如果用科特林

我的简单解决方案

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

只是定位或放置你需要从活动中获得的数据。

Adapter constructor:

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

在项中的 AdapteronClickLictener 中
在 BindViewHolder 上

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

现在无论何时你改变回收视图中的位置,它都保持在 Holder 中(或者应该称为 Listener)

我希望它能派上用场

我的第一个帖子

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

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

In my case, I do that in an onClick listener put on that vh View. IMHO, the recycleView class misses out in that .getPosition() and other features that we know from ListView, and that are sometimes mandatory, are simply not available. I strongly regret having moved from ListView to Recycle ditto. The timethief cost was more than a day to unveil its mysteries. Bad engineering. (But what is there is ok)