简体   繁体   English

获取点击的项目及其在 RecyclerView 中的位置

[英]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 .我正在用RecyclerView替换我的ListView ,列表显示正常,但我想知道如何获得点击的项目及其位置,类似于我们使用的方法OnItemClickListener.onItemClick(AdapterView parent, View v, int position, long id) ListView

Thanks for ideas!感谢您的想法!

Based on the link: Why doesn't RecyclerView have onItemClickListener()?基于链接: 为什么 RecyclerView 没有 onItemClickListener()? and How RecyclerView is different from Listview? 以及 RecyclerView 与 Listview 有何不同? , and also @Duncan's general idea, I give my solution here: ,还有@Duncan 的总体思路,我在这里给出我的解决方案:

  • Define one interface RecyclerViewClickListener for a passing message from the adapter to Activity / Fragment :定义一个接口RecyclerViewClickListener以将消息从适配器传递到Activity / Fragment

     public interface RecyclerViewClickListener { public void recyclerViewListClicked(View v, int position); }
  • In Activity / Fragment implement the interface, and also pass listener to adapter:Activity / Fragment实现接口,并将侦听器传递给适配器:

     @Override public void recyclerViewListClicked(View v, int position){... ...} //set up adapter and pass clicked listener this myAdapter = new MyRecyclerViewAdapter(context, this);
  • In Adapter and ViewHolder :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()); } } }

After testing, it works fine.经过测试,它工作正常。

[ UPDATE ] [更新]

Since API 22, RecyclerView.ViewHolder.getPosition() is deprecated, so instead with getLayoutPosition() .从 API 22 开始, RecyclerView.ViewHolder.getPosition()已被弃用,因此使用getLayoutPosition()代替。

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

Here is an Example to set a Click Listener.这是设置单击侦听器的示例。

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

    }

Put this code where you define recycler view in 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();
                }
            })
    );

Then make separate class and put this code:然后创建单独的类并放置以下代码:

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

    }
}

create java file with below code使用以下代码创建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) {

}

and just use the listener on your RecyclerView object.并在您的 RecyclerView 对象上使用侦听器。

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

If you want Click event of recycle-View from activity/fragment instead of adapter then you can also use following short cut way.如果您想要来自活动/片段而不是适配器的回收视图的 Click 事件,那么您也可以使用以下快捷方式。

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

You can also use other long ways like using interface您还可以使用其他长的方式,如使用界面

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

Use below code:-使用以下代码:-

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

    }
}
}

Here is the simplest and the easiest way to find the position of the clicked item:这是查找被点击项目位置的最简单和最简单的方法:

I've also faced the same problem.我也遇到过同样的问题。

I wanted to find of the position of the clicked/selected item of the RecyclerView() and perform some specific operations on that particular item.我想找到 RecyclerView() 的点击/选择项目的位置,并对该特定项目执行一些特定操作。

getAdapterPosition() method works like a charm for these kind of stuff. getAdapterPosition() 方法对于这些东西来说就像一个魅力。 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();

You do not have to use any extra method.您不必使用任何额外的方法。 Just create a global variable named 'position' and initialize it with getAdapterPosition() in any of the major method of the adapter (class or similar).只需创建一个名为“position”的全局变量,并在适配器的任何主要方法(类或类似方法)中使用 getAdapterPosition() 对其进行初始化。

Here is a brief documentation from this link .这是来自此链接的简要文档。

getAdapterPosition added in version 22.1.0 int getAdapterPosition () Returns the Adapter position of the item represented by this ViewHolder. 22.1.0 版本新增 getAdapterPosition int getAdapterPosition() 返回此 ViewHolder 所代表的 item 的 Adapter 位置。 Note that this might be different than the getLayoutPosition() if there are pending adapter updates but a new layout pass has not happened yet.请注意,如果存在挂起的适配器更新但尚未发生新的布局传递,则这可能与 getLayoutPosition() 不同。 RecyclerView does not handle any adapter updates until the next layout traversal. RecyclerView 在下一次布局遍历之前不处理任何适配器更新。 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.这种不一致并不重要,因为它会小于 16 毫秒,但如果您想使用 ViewHolder 位置来访问适配器,则可能会出现问题。 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.在这种情况下,您应该使用此方法来计算 ViewHolder 的适配器位置。

Happy to help.乐于帮助。 Feel free to ask doubts.随意提出疑问。

My simple solution我的简单解决方案

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

Just position or put data you need to get from activity.只需定位或放置您需要从活动中获取的数据。

Adapter constructor:适配器构造函数:

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

In Activity:在活动中:

 @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        selectedPosition = 0;

        positionHolder = new PositionHolder(selectedPosition);
        initView();
    }

In Adapter onClickLictener in the item在 Adapter onClickLictener 项中
in onBindViewHolder在 onBindViewHolder

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

Now whenever you change position in RecyclerView it is hold in the Holder (or maybe it should be called Listener)现在,每当您在 RecyclerView 中更改位置时,它都会保留在 Holder 中(或者它应该被称为 Listener)

I hope it will be usefull我希望它会很有用

My first post ;P我的第一篇文章;P

RecyclerView doesn't provide such method. RecyclerView 不提供这种方法。

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为了管理 RecyclerView 上的点击事件,我最终在我的适配器中实现了 onClickListener,当绑定 ViewHolder 时:在我的 ViewHolder 中,我保留对根视图的引用(就像你可以使用你的 ImageViews、TextViews 等......)和绑定时我在 viewHolder 上设置了一个标签,其中包含我需要处理点击(例如位置)和点击侦听器的信息

Everytime I use another approach.每次我使用另一种方法。 People seem to store or get position on a view, rather than storing a reference to an object that is displayed by ViewHolder.人们似乎在视图上存储或获取位置,而不是存储对 ViewHolder 显示的对象的引用。

I use this approach instead, and just store it in ViewHolder when onBindViewHolder() is called, and set reference to null in onViewRecycled().我改用这种方法,并在调用 onBindViewHolder() 时将其存储在 ViewHolder 中,并在 onViewRecycled() 中将引用设置为 null。

Every time ViewHolder becomes invisible, it's recycled.每次 ViewHolder 变得不可见时,它都会被回收。 So this doesn't affect in large memory consumption.所以这不会影响大量内存消耗。

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

From the designer, you can set the onClick property of the listItem to a method defined with a single parameter.在设计器中,您可以将 listItem 的onClick属性设置为使用单个参数定义的方法。 I have an example method defined below.我有一个下面定义的示例方法。 The method getAdapterPosition will give you the index of the selected listItem. getAdapterPosition 方法将为您提供所选列表项的索引。

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

For information on setting up a RecyclerView, see the documentation here: https://developer.android.com/guide/topics/ui/layout/recyclerview有关设置 RecyclerView 的信息,请参阅此处的文档: https : //developer.android.com/guide/topics/ui/layout/recyclerview

The simple (but not so obvious) solution is to do this:简单(但不是那么明显)的解决方案是这样做:

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

and then, whenever, call the method currentPosition = vh.getLayoutPosition();然后,无论何时,调用方法currentPosition = vh.getLayoutPosition();

In my case, I do that in an onClick listener put on that vh View.在我的情况下,我在一个 onClick 监听器中执行该操作,该监听器放置在该 vh 视图上。 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.恕我直言,recycleView 类错过了 .getPosition() 和我们从 ListView 知道的其他功能,有时是强制性的,根本不可用。 I strongly regret having moved from ListView to Recycle ditto.我非常后悔从 ListView 转移到 Recycle 同上。 The timethief cost was more than a day to unveil its mysteries.时间窃贼花费了一天多的时间来揭开它的神秘面纱。 Bad engineering.糟糕的工程。 (But what is there is ok) (但有什么就可以了)

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

        }
    });
}

Try in this way用这种方法试试

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

} }

In your Activity or fragment :在您的 Activity 或片段中:

ContentAdapter adapter = new ContentAdapter(itemList, this);

Note : Implement the OnItemClickListener based on context given by you in activity or fragment and overide methods.注意:根据您在活动或片段和覆盖方法中提供的上下文实现 OnItemClickListener。

Short extension for Kotlin Kotlin 的简短扩展
Method returns absolute position of all items (not the position of only visible items).方法返回所有项目的绝对位置(不是仅可见项目的位置)。

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

And usage和用法

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

Use getLayoutPosition() in your custom interface java method.在您的自定义接口 java 方法中使用 getLayoutPosition()。 This will return the selected position of an item, check full detail on这将返回一个项目的选定位置,检查完整的细节

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

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

After lot of trial and error I found that there is a very easy way to do this that is by creating an interface in adapter class and implementing that in fragment now here comes the twist, I instansiated the view model inside my override function present In the fragment now you can send the data from that function to viemodel and from there anywhere.经过大量的反复试验,我发现有一种非常简单的方法可以做到这一点,即通过在适配器类中创建一个接口并在片段中实现它,现在出现了转折,我在我的覆盖函数中实例化了视图模型。片段现在您可以将数据从该函数发送到 viemodel 并从那里发送到任何地方。

I don't know if this method is good coding method but please let me know in comment and if any wants to see the let me know in the comment section I regularly open stackover flow.我不知道这种方法是否是好的编码方法,但请在评论中告诉我,如果有人想在评论部分看到让我知道,我会定期打开 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")
                }

            })

If use Kotlin如果使用 Kotlin

In onViewHolder set onClickListiner to any view and in side click use this code :在 onViewHolder 中将 onClickListiner 设置为任何视图,并在侧点击中使用以下代码:

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

Replace Drawer_Bar with your Activity name.Drawer_Bar替换为您的活动名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM