如何在 RecyclerView 中选择位置?

Posted

技术标签:

【中文标题】如何在 RecyclerView 中选择位置?【英文标题】:How do I get the position selected in a RecyclerView? 【发布时间】:2014-12-28 05:20:21 【问题描述】:

我正在试验支持库的 recyclerview 和卡片。我有卡片的recyclerview。每张卡片的右上角都有一个“x”图标,可将其移除:

卡片xml,list_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:layout_margin="5dp">
<RelativeLayout
    android:layout_
    android:layout_>
    <TextView
        android:id="@+id/taskDesc"
        android:layout_
        android:layout_
        android:layout_alignParentBottom="true"
        android:textSize="40sp"
        android:text="hi"/>
    <ImageView
        android:id="@+id/xImg"
        android:layout_
        android:layout_
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:src="@drawable/ic_remove"/>
</RelativeLayout>
</android.support.v7.widget.CardView>

我试图用notifyItemRemoved(position)TaskAdapter.java 中的位置标记该行:

public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskViewHolder>  

private List<Task> taskList;
private TaskAdapter thisAdapter = this;


// cache of views to reduce number of findViewById calls
public static class TaskViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener 
    protected TextView taskTV;
    protected ImageView closeBtn;
    public TaskViewHolder(View v) 
        super(v);
        taskTV = (TextView)v.findViewById(R.id.taskDesc);
    

    @Override
    public void onClick(View v) 
        int position = v.getTag();
        adapter.notifyItemRemoved(position);
    



public TaskAdapter(List<Task> tasks) 
    if(tasks == null)
        throw new IllegalArgumentException("tasks cannot be null");
    taskList = tasks;



// onBindViewHolder binds a model to a viewholder
@Override
public void onBindViewHolder(TaskViewHolder taskViewHolder, int pos) 
    final int position = pos;
    Task currTask = taskList.get(pos);
    taskViewHolder.taskTV.setText(currTask.getDescription());

    taskViewHolder.closeBtn.setOnClickListener(new View.OnClickListener() 
        @Override
        public void onClick(View v) 
            thisAdapter.notifyItemRemoved(position);
        
    );


@Override
public int getItemCount() 
    return taskList.size();



// inflates row to create a viewHolder
@Override
public TaskViewHolder onCreateViewHolder(ViewGroup parent, int pos) 
    View itemView = LayoutInflater.from(parent.getContext()).
                                   inflate(R.layout.list_item, parent, false);

    return new TaskViewHolder(itemView);


这不起作用,因为您无法设置标签,也无法从onClick.访问适配器

【问题讨论】:

【参考方案1】:

将您的onClickListeners 设置为onBindViewHolder(),您就可以从那里访问该职位。如果您将它们设置在您的ViewHolder 中,您将不知道点击了哪个位置,除非您还将该位置传递给ViewHolder

编辑

正如pskink 指出的ViewHolder 有一个getPosition(),所以你最初的做法是正确的。

点击视图时,您可以在ViewHolder 中使用getPosition() 并返回位置

更新

getPosition() 现已弃用并替换为 getAdapterPosition()

2020 年更新

getAdapterPosition() 现在已弃用并替换为 getAbsoluteAdapterPosition()getBindingAdapterPosition()

Kotlin 代码:

override fun onBindViewHolder(holder: MyHolder, position: Int) 
        // - get element from your dataset at this position
        val item = myDataset.get(holder.absoluteAdapterPosition)
    

【讨论】:

你能显示一些代码吗?如果 taskViewHolder 没有 setOnClickListener 方法,我该怎么做? 您使用那里的viewholder中的视图 @tyczj 不,更好的地方是在 ViewHolder ctor 中设置 listener,ViewHolder 知道自己的位置 @pskink 如果不将位置传递给构造函数,您将如何知道位置呢? @tyczj ViewHolder.getPosition(),顺便说一句,当你创建一个 ViewHolder 时你不知道位置,位置是在 onBindViewHolder 中设置的【参考方案2】:

另一种方法 - 使用 View 类的 setTag()getTag() 方法。

    在适配器的 onBindViewHolder 方法中使用 setTag()

    @Override
    public void onBindViewHolder(myViewHolder viewHolder, int position) 
        viewHolder.mCardView.setTag(position);
    
    

    mCardView 在 myViewHolder 类中定义

    private class myViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener 
               public View mCardView;
    
               public myViewHolder(View view) 
                   super(view);
                   mCardView = (CardView) view.findViewById(R.id.card_view);
    
                   mCardView.setOnClickListener(this);
               
           
    

    在您的 OnClickListener 实现中使用 getTag()

    @Override
    public void onClick(View view) 
        int position = (int) view.getTag();           
    
    //display toast with position of cardview in recyclerview list upon click
    Toast.makeText(view.getContext(),Integer.toString(position),Toast.LENGTH_SHORT).show();
    
    

查看https://***.com/a/33027953/4658957了解更多详情

【讨论】:

太棒了!简单而最好的【参考方案3】:

补充@tyczj 答案:

通用适配器伪代码:

public abstract class GenericRecycleAdapter<T, K extends RecyclerView.ViewHolder> extends RecyclerView.Adapter 

private List<T> mList;
//default implementation code 

public abstract int getLayout();

@Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        View v = LayoutInflater.from(parent.getContext())
                .inflate(getLayout(), parent, false);
        return getCustomHolder(v);
    

    public Holders.TextImageHolder getCustomHolder(View v) 
        return new Holders.TextImageHolder(v)
            @Override
            public void onClick(View v) 
                onItem(mList.get(this.getAdapterPosition()));
            
        ;
    

abstract void onItem(T t);

 @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) 
    onSet(mList.get(position), (K) holder);



public abstract void onSet(T item, K holder);


ViewHolder:

public class Holders  

    public static class TextImageHolder extends RecyclerView.ViewHolder implements View.OnClickListener

        public TextView text;

        public TextImageHolder(View itemView) 
            super(itemView);
            text = (TextView) itemView.findViewById(R.id.text);
            text.setOnClickListener(this);


        

        @Override
        public void onClick(View v) 

        
    



适配器使用:

public class CategoriesAdapter extends GenericRecycleAdapter<Category, Holders.TextImageHolder> 


    public CategoriesAdapter(List<Category> list, Context context) 
        super(list, context);
    

    @Override
    void onItem(Category category) 

    


    @Override
    public int getLayout() 
        return R.layout.categories_row;
    

    @Override
    public void onSet(Category item, Holders.TextImageHolder holder) 

    




【讨论】:

【参考方案4】:
 public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener 

    FrameLayout root;


    public ViewHolder(View itemView) 
        super(itemView);

        root = (FrameLayout) itemView.findViewById(R.id.root);
        root.setOnClickListener(this);
    


    @Override
    public void onClick(View v) 
        LogUtils.errorLog("POS_CLICKED: ",""+getAdapterPosition());
    

【讨论】:

【参考方案5】:

就我个人而言,我发现并且最适合我的最简单方法如下:

在“RecycleAdapter”类(子类)中创建一个接口

public interface ClickCallback 
    void onItemClick(int position);

在构造函数中添加接口的变量作为参数。

private String[] items;
private ClickCallback callback;

public RecyclerAdapter(String[] items, ClickCallback clickCallback) 
    this.items = items;
    this.callback = clickCallback;

在 ViewHolder(另一个子类)中设置 Click 监听器,并通过接口传递“位置”

    AwesomeViewHolder(View itemView) 
        super(itemView);
        itemView.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View v) 
                callback.onItemClick(getAdapterPosition());
            
        );
        mTextView = (TextView) itemView.findViewById(R.id.mTextView);
    

现在,在活动/片段中初始化回收器适配器时,只需创建一个新的“ClickCallback”(接口)

String[] values = "Hello","World";
RecyclerAdapter recyclerAdapter = new RecyclerAdapter(values, new RecyclerAdapter.ClickCallback() 
    @Override
    public void onItemClick(int position) 
         // Do anything with the item position
    
);

对我来说就是这样。 :)

【讨论】:

【参考方案6】:

我是这样解决的

class MyOnClickListener implements View.OnClickListener 
        @Override
        public void onClick(View v) 

            int itemPosition = mRecyclerView.getChildAdapterPosition(v);

            myResult = results.get(itemPosition);


        
    

在适配器中

@Override
        public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                       int viewType)             
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_wifi, parent, false);
            v.setOnClickListener(new MyOnClickListener());
            ViewHolder vh = new ViewHolder(v);
            return vh;
        

【讨论】:

请详细说明你的答案,你在代码中做了什么。【参考方案7】:

获得专注的孩子,并使用它来获得适配器中的位置。

mRecyclerView.getChildAdapterPosition(mRecyclerView.getFocusedChild())

【讨论】:

【参考方案8】:

1.创建类名 RecyclerTouchListener.java

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener 


private GestureDetector gestureDetector;
private ClickListener clickListener;

public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) 
    this.clickListener = clickListener;
    gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() 
        @Override
        public boolean onSingleTapUp(MotionEvent e) 
            return true;
        

        @Override
        public void onLongPress(MotionEvent e) 
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null && clickListener != null) 
                clickListener.onLongClick(child, recyclerView.getChildAdapterPosition(child));
            
        
    );


@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) 

    View child = rv.findChildViewUnder(e.getX(), e.getY());
    if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) 
        clickListener.onClick(child, rv.getChildAdapterPosition(child));
    
    return false;


@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) 


@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) 



public interface ClickListener 
    void onClick(View view, int position);

    void onLongClick(View view, int position);


2。调用 RecyclerTouchListener

recycleView.addOnItemTouchListener(new RecyclerTouchListener(this, recycleView, 
new RecyclerTouchListener.ClickListener() 
    @Override
    public void onClick(View view, int position) 
        Toast.makeText(MainActivity.this,Integer.toString(position),Toast.LENGTH_SHORT).show();
    

    @Override
    public void onLongClick(View view, int position) 

    
));

【讨论】:

【参考方案9】:

我认为获取项目位置最正确的方法是

View.OnClickListener onClickListener = new View.OnClickListener() 
    @Override public void onClick(View v) 
      View view = v;
      View parent = (View) v.getParent();
      while (!(parent instanceof RecyclerView))
        view=parent;
        parent = (View) parent.getParent();
      
      int position = recyclerView.getChildAdapterPosition(view);

因为视图,您单击的并不总是行布局的根视图。如果视图不是根视图(例如按钮),您将获得 Class cast 异常。因此,首先我们需要找到视图,它是您的 reciclerview 的直接孩子。然后,使用 recyclerView.getChildAdapterPosition(view); 找到位置;

【讨论】:

【参考方案10】:

onBindViewHolder() 会为每个项目调用,并且在 onBindVieHolder() 中设置点击侦听器是不必要的重复选项,您可以在 ViewHolder 构造函数中调用一次。

public class MyViewHolder extends RecyclerView.ViewHolder 
      implements View.OnClickListener
   public final TextView textView; 

   public MyViewHolder(View view)
      textView = (TextView) view.findViewById(R.id.text_view);
      view.setOnClickListener(this);
      // getAdapterPosition() retrieves the position here.
    

   @Override
   public void onClick(View v)
      // Clicked on item 
      Toast.makeText(mContext, "Clicked on position: " + getAdapterPosition(), Toast.LENGTH_SHORT).show();
   

【讨论】:

【参考方案11】:

无需让您的 ViewHolder 实现 View.OnClickListener。您可以通过在 RecyclerView.Adapter 的 onCreateViewHolder 方法中设置点击监听器直接获取点击位置,这里是代码示例:

public class ItemListAdapterRecycler extends RecyclerView.Adapter<ItemViewHolder>


    private final List<Item> items;

    public ItemListAdapterRecycler(List<Item> items)
    
        this.items = items;
    

    @Override
    public ItemViewHolder onCreateViewHolder(final ViewGroup parent, int viewType)
    
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_row, parent, false);

        view.setOnClickListener(new View.OnClickListener()
        
            @Override
            public void onClick(View view)
            
                int currentPosition = getClickedPosition(view);
                Log.d("DEBUG", "" + currentPosition);
            
        );

        return new ItemViewHolder(view);
    

    @Override
    public void onBindViewHolder(ItemViewHolder itemViewHolder, int position)
    
        ...
    

    @Override
    public int getItemCount()
    
        return items.size();
    

    private int getClickedPosition(View clickedView)
    
        RecyclerView recyclerView = (RecyclerView) clickedView.getParent();
        ItemViewHolder currentViewHolder = (ItemViewHolder) recyclerView.getChildViewHolder(clickedView);
        return currentViewHolder.getAdapterPosition();
    


【讨论】:

【参考方案12】:
@Override
public void onClick(View v) 
     int pos = getAdapterPosition();

就这么简单,ViewHolder

【讨论】:

【参考方案13】:

使用数据绑定时,您需要从项目的点击侦听器内部知道 RecyclerView 点击位置:

科特林

    val recyclerView = view.parent as RecyclerView
    val position = recyclerView.getChildAdapterPosition(view)

【讨论】:

以上是关于如何在 RecyclerView 中选择位置?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 RecyclerView 适配器中获取第二个模型的位置

如何在recyclerview中定义滚动条视图的位置?

如何使用Recyclerview中单击的适配器位置获取数据?

如何根据其在 onCreateViewHolder 方法中的位置在 RecyclerView 中膨胀不同的布局

使用 PagingDataAdapter 时如何恢复 recyclerview 滚动位置?

在 RecyclerView 中使用多个视图类型时如何将项目添加到所需的 ViewHolder 位置?