有人使用RecyclerView找到了一种方法来设置一个onClickListener的项目在RecyclerView? 我想设置一个监听器为每个项目的布局,但这似乎有点太麻烦了 我确信有一种方法让RecyclerView监听onClick事件,但我不能完全弄清楚。


当前回答

对于kotlin句柄,根据Jacobs的回答单击RecyclerView答案

创建类RecyclerItemClickListener:

class RecyclerItemClickListener(context: Context, recyclerView: RecyclerView, listner: OnItemClickListener) : RecyclerView.OnItemTouchListener {

    var mGestureDetector: GestureDetector
    var mListner: OnItemClickListener
    
    interface OnItemClickListener {
        fun onItemClick(view: View, position: Int)
        fun onLongItemClick(view: View, position: Int)
    }

    init {
        this.mListner = listner
        mGestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
            override fun onSingleTapUp(e: MotionEvent?): Boolean = true
            
            override fun onLongPress(e: MotionEvent?) {
                val child: View? = recyclerView.findChildViewUnder(e!!.getX(), e.getY())
                if (child != null && mListner != null) {
                    mListner.onLongItemClick(child, recyclerView.getChildAdapterPosition(child))
                }
            }
        })
    }

    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) = Unit

    override fun onInterceptTouchEvent(view: RecyclerView, e: MotionEvent): Boolean {
        val childView: View? = view.findChildViewUnder(e!!.getX(), e.getY())
        if (childView != null && mListner != null && mGestureDetector.onTouchEvent(e)) {
            mListner.onItemClick(childView, view.getChildAdapterPosition(childView))
            return true
        }
        return false
    }

    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) = Unit

}

点击Any RecyclerView(在活动/片段内):

recyclerView.addOnItemTouchListener(
    RecyclerItemClickListener(this, recyclerView, object : RecyclerItemClickListener.OnItemClickListener {
        override fun onItemClick(view: View, position: Int) {
            // TODO catch click
        }

        override fun onLongItemClick(view: View, position: Int) {
            // TODO catch click
        }
    })
)

其他回答

对我来说,这是最好的方法:

class YourRecyclerAdapter extends RecyclerView.Adapter<ContactViewHolder> implements View.OnClickListener { 
  ...
  @Override
  public void onClick(View view) {
        int itemPosition = vRecycle.getChildPosition(view);
        //And use itemPosition to get the item from your collection. This way you dont restrain the ViewHolder with a OnClick callback
    }
  ...
}

通常你在CardView中有多个元素,所以你需要一个布局视图来包装和组织它们。你可以添加一个OnClickListener到那个布局视图。1. 添加一个id到你的布局。在这个例子中是线性布局

<android.support.v7.widget.CardView
 .....>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/card_view_linearLayout">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="name"
            android:id="@+id/card_view_name" />

        ...

    </LinearLayout>

</android.support.v7.widget.CardView>

2美元。在你的内部ViewHolder类中获取布局视图。

public static class ViewHolder extends RecyclerView.ViewHolder{
    private TextView nameView;
    ...
    private LinearLayout linearLayout;
    public ViewHolder(View itemView) {
        super(itemView);
        nameView = (TextView)itemView.findViewById(R.id.card_view_name);
        ...
        linearLayout = (LinearLayout)itemView.findViewById(R.id.card_view_linearLayout);
    }
}

3美元。将监听器添加到onBindViewHolder中的布局中,并使用回调将数据发送到活动或片段(未测试)。

@Override
public void onBindViewHolder(TrackAdapter.ViewHolder holder, final int position) {
    String str = mStringList.get(position);

    holder.nameView.setText(str);
    ...
    holder.linearLayout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            callback.itemCallback(mStringList.get(position));
        }
    });
}

如何使用回调是另一个故事

我已经看了所有的答案,并不是很满意。我找到了更简单快捷的方法。想分享给未来的读者。

选择单个回收项目中的任何视图。 获取这个View的父视图(确保你施放了合适的ViewGroup) 将onClickListener设置为这个父节点。

示例代码(它是在适配器的onBindViewHolder方法中编写的):

@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {

    ConstraintLayout parent = (ConstraintLayout) holder.title.getParent();
    parent.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(context, "Clicked recycler view item at position " + position, Toast.LENGTH_SHORT).show();
        }
    });
}

这里有一个策略,它给出了一个类似于ListView实现的结果,因为你可以在活动或片段级别而不是适配器或ViewHolder级别定义侦听器。它还定义了一些抽象类,这些抽象类负责适配器和持有者的大量样板工作。

抽象类

首先,定义一个抽象Holder,它扩展了RecyclerView。并定义了一个泛型数据类型T,用于将数据绑定到视图。bindViews方法将由一个子类实现,用于将数据映射到视图。

public abstract class Holder<T> extends RecyclerView.ViewHolder {
    T data;

    public Holder(View itemView) {
        super(itemView);
    }

    public void bindData(T data){
        this.data = data;
        bindViews(data);
    }

    abstract protected void bindViews(T data);
}

同样,创建一个抽象适配器,扩展RecyclerView.Adapter<Holder<T>>。这定义了3个接口方法中的2个,子类将需要实现最后一个onViewHolderCreated方法。

public abstract class Adapter<T> extends RecyclerView.Adapter<Holder<T>> {
    List<T> list = new ArrayList<>();

    @Override
    public void onBindViewHolder(Holder<T> holder, int position) {
        holder.bindData(list.get(position));
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    public T getItem(int adapterPosition){
        return list.get(adapterPosition);
    }
}

具体类

现在创建一个扩展Holder的新具体类。该方法只需定义视图并处理绑定。这里我使用ButterKnife库,但请随意使用itemView.findViewById(…)方法代替。

public class PersonHolder extends Holder<Person>{
    @Bind(R.id.firstname) TextView firstname;
    @Bind(R.id.lastname) TextView lastname;

    public PersonHolder(View view){
        super(view);
        ButterKnife.bind(this, view);
    }

    @Override
    protected void bindViews(Person person) {
        firstname.setText(person.firstname);
        lastname.setText(person.lastname);
    }
}

最后,在持有RecyclerView的Activity或Fragment类中,你会有这样的代码:

// Create adapter, this happens in parent Activity or Fragment of RecyclerView
adapter = new Adapter<Person>(){
    @Override
    public PersonHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.layout_person_view, parent, false);

        PersonHolder holder = new PersonHolder(v);
        v.setOnClickListener(new OnClickListener(){
            @Override
            public void onClick(View v) {
                int itemPos = holder.getAdapterPosition();
                Person person = getItem(itemPos);

                // do something with person
                EventBus.getDefault().postSticky(new PersonClickedEvent(itemPos, person));
            }
        });

        return holder;
    }
};

将类标记为抽象并实现OnClick方法

public abstract class MainGridAdapter extends
    RecyclerView.Adapter<MainGridAdapter.ViewHolder> {
private List<MainListItem> mDataset;

// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder {
    // each data item is just a string in this case
    public TextView txtHeader;
    public TextView txtFooter;

    public ViewHolder(View v) {
        super(v);
        txtHeader = (TextView) v.findViewById(R.id.firstLine);
        txtFooter = (TextView) v.findViewById(R.id.secondLine);
    }
}

public void add(int position, MainListItem item) {
    mDataset.add(position, item);
    notifyItemInserted(position);
}

public void remove(MainListItem item) {
    int position = mDataset.indexOf(item);
    mDataset.remove(position);
    notifyItemRemoved(position);
}

// Provide a suitable constructor (depends on the kind of dataset)
public MainGridAdapter(List<MainListItem> myDataset) {
    mDataset = myDataset;
}

// Create new views (invoked by the layout manager)
@Override
public MainGridAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
        int viewType) {
    // create a new view
    View v = LayoutInflater.from(parent.getContext()).inflate(
            R.layout.list_item_grid_line, parent, false);
    // set the view's size, margins, paddings and layout parameters
    ViewHolder vh = new ViewHolder(v);
    return vh;
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    // - get element from your dataset at this position
    // - replace the contents of the view with that element     
    OnClickListener clickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            onItemClicked(position);
        }
    };
    holder.itemView.setOnClickListener(clickListener);
    holder.txtHeader.setOnClickListener(clickListener);
    holder.txtFooter.setOnClickListener(clickListener);
    final MainListItem item = mDataset.get(position);
    holder.txtHeader.setText(item.getTitle());
    if (TextUtils.isEmpty(item.getDescription())) {
        holder.txtFooter.setVisibility(View.GONE);
    } else {
        holder.txtFooter.setVisibility(View.VISIBLE);
        holder.txtFooter.setText(item.getDescription());
    }
}

// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
    return mDataset.size();
}

public abstract void onItemClicked(int position);

}

在绑定事件中实现单击处理程序,使其只有一个事件实现

执行此:

mAdapter = new MainGridAdapter(listItems) {         
    @Override
    public void onItemClicked(int position) {
        showToast("Item Clicked: " + position, ToastPlus.STYLE_INFO);
    }
};

同样可以做长时间点击