RecyclerView的OnClickListener

问题描述 投票:1回答:6

与ListView不同,Android RecyclerView似乎过于复杂而无法实现。由于RecyclerView子系统没有OnItemClickListener,我一直在尝试实现以下注册点击事件:

final RecyclerView rv=(RecyclerView)findViewById(R.id.recycler_view);
    LinearLayoutManager llm=new LinearLayoutManager(this);
    rv.setLayoutManager(llm);
    MyRVAdapter rva=new MyRVAdapter(persons);
    rv.setAdapter(rva);

    rv.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int itemPosition = rv.indexOfChild(v);
            Log.d("Tag", String.valueOf(itemPosition));
        }
    });

出于某种原因,我无法使用此代码。点击事件根本没有注册!谁能告诉我我做错了什么?我已经看到了一些解决方案,但我认为这应该有效。谢谢你的帮助!

android view onclicklistener android-recyclerview
6个回答
1
投票

而不是为整个RecyclerView设置onclicklistener设置它在你的ViewHolderAdapter.类的构造函数内

一些示例代码将类似于以下类,它是recyclerview适配器中的内部类。

     class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

      .....
        public ViewHolder(View itemView) {
            super(itemView);
           .......
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {

           .......
        }
    }

在适配器的onCreateViewHolder内部,将膨胀的视图传递给ViewGroup的构造函数,例如,

 @Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View v = LayoutInflater.from(viewGroup.getContext())
            .inflate(R.layout.your_layout, viewGroup, false);
    ViewHolder viewHolder = new ViewHolder(v);
    return viewHolder;
}    

2
投票

谢谢大家!我想出了一个解决方案(尽管它对我有用,但我不确定该技术的效率如何)。我在View.OnClickListener课程中使用RecyclerView.Adapter界面。为了防止大量垃圾收集,我在onCreateViewHolderRecyclerView.Adapter方法中分配了点击监听器。这是我的RecyclerView.Adapter类的完整实现:

package com.example.evinish.recyclerdemo;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

/**
 * Created by evinish on 9/25/2015.
 */
public class MyRVAdapter extends RecyclerView.Adapter<MyViewHolder> implements View.OnClickListener{

    List<Person> persons;

    public MyRVAdapter(List<Person> persons) {
        this.persons = persons;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout,parent,false);
        MyViewHolder vh=new MyViewHolder(view);
        view.setOnClickListener(this);

        return vh;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.company.setText(persons.get(position).getCompany());
        holder.occupation.setText(persons.get(position).getOccupation());
        holder.name.setText(persons.get(position).getName());


    }

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

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }

    public void clearAdapter() {
        persons.clear();
        notifyDataSetChanged();
    }

    @Override
    public void onClick(View v) {
        TextView clickedName=(TextView)v.findViewById(R.id.card_text1);
        Toast.makeText(v.getContext(),clickedName.getText().toString(),Toast.LENGTH_LONG).show();

    }
}

如果有更好的方法在RecyclerView注册点击事件,请告诉我。


2
投票

虽然已经有了一些答案,但我想我可能会提供我的实现以及解释。 (详情可在another similar question I answered上找到)。

因此,要添加单击侦听器,您的内部ViewHolder类需要实现View.OnClickListener。这是因为你将OnClickListener设置为itemView构造函数的ViewHolder参数。让我告诉你我的意思:

public class ExampleClickViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    TextView text1, text2;

    ExampleClickViewHolder(View itemView) {
        super(itemView);

        // we do this because we want to check when an item has been clicked:
        itemView.setOnClickListener(this);

        // now, like before, we assign our View variables
        title = (TextView) itemView.findViewById(R.id.text1);
        subtitle = (TextView) itemView.findViewById(R.id.text2);
    }

    @Override
    public void onClick(View v) {
        // The user may not set a click listener for list items, in which case our listener
        // will be null, so we need to check for this
        if (mOnEntryClickListener != null) {
            mOnEntryClickListener.onEntryClick(v, getLayoutPosition());
        }
    }
}

您需要添加的唯一其他内容是Adapter的自定义界面和setter方法:

private OnEntryClickListener mOnEntryClickListener;

public interface OnEntryClickListener {
    void onEntryClick(View view, int position);
}

public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
    mOnEntryClickListener = onEntryClickListener;
}

所以你的新点击支持Adapter已经完成了。

现在,让我们用它......

    ExampleClickAdapter clickAdapter = new ExampleClickAdapter(yourObjects);
    clickAdapter.setOnEntryClickListener(new ExampleClickAdapter.OnEntryClickListener() {
        @Override
        public void onEntryClick(View view, int position) {
            // stuff that will happen when a list item is clicked
        }
    });

它基本上是你如何设置一个普通的Adapter,除了你使用你创建的setter方法来控制当你的用户点击特定列表项时你将做什么。

我在this Gist on GitHub上创建了一组示例,它们显示了可以用作模板的完整Java文件,或者帮助您了解Adapter的工作原理。


2
投票

看来你已经得到了问题的答案,但这里的答案都没有尝试使用RxJava来解决这个问题。

我自己是RxJava的忠实粉丝,我永远不会错过任何可能使用它的机会。

这是我用的,

public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    String[] mDataset = { "Data", "In", "Adapter" };

    private final PublishSubject<String> onClickSubject = PublishSubject.create();

    @Override 
    public void onBindViewHolder(final ViewHolder holder, int position) {
        final String element = mDataset[position];

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               onClickSubject.onNext(element);
            }
        });
    }

    public Observable<String> getPositionClicks(){
        return onClickSubject.asObservable();
    }
}

它公开了一个Observable来拦截点击事件。您现在可以完全控制您想要对点击事件执行的操作(还记得RxJava操作符吗?)。

你为什么不尝试一下?


1
投票

您应该在RecyclerView中的各个视图上设置单击侦听器,而不是整个RecyclerView。这可以通过在ViewHolder类的构造函数中设置侦听器来实现,类似于以下示例:

class ViewHolder extends RecyclerView.ViewHolder {
    public ViewHolder(View view, OnClickListener listener) {
        super(view);             
        view.setOnClickListener(listener);
    }
}

1
投票

晚会,但也许这将有助于其他人。

我发现将OnClickListener附加到RecyclerView中项目或儿童视图的最佳位置是在RecyclerView.AdapteronCreateViewHolder中。由于RecyclerView回收/重用项目ViewHolders,它是创建/附加监听器的最有效的地方,因为它们仅在创建新的持有者时创建和附加,因此比onBindViewHolder更频繁甚至绑定(在持有者内部)类)。

但是,只需要将更改注册到特定数据集项(适配器数据阵列/列表中特定位置的项),需要2-3件事。

如果您有多种ViewHolder类型,并且该信息有助于确定单击时发生的情况。那么你需要

  1. ViewHolder类型或访问Adapter.getViewHolderType()

您需要的更明显的事情是点击位置的数据集(数组/列表)项目,这样您就可以在该项目中注册特定于项目的更改(以便在项目滚动屏幕时更改保留在项目中) 。这意味着您需要访问

  1. RecyclerView和的数据集(通常是数组或列表)
  2. 数据集中特定项目的位置,需要更改

您也可能需要调用notifyItemChanged()notifyItemInserted()或(效率较低的notifyDataSetChanged()

例如,在我的一个项目的适配器中:

// Inflates the appropriate layout according to the ViewType.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view;
    if (viewType == HOLDER_VIEW_TYPE_ONE) {          
        view = LayoutInflater.from(mContext).inflate(R.layout.type_one_holder_layout, parent, false);
        final RecyclerView.ViewHolder holder = new TypeOneHolder(mContext,view);

        //Place onclick listener after holder. holder needs to be final
        //but thats fine as onCreateViewHolder usually just returns holder
        //right after its created. onBindViewHolder is where changes are
        //made.
        view.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View view) {
                final int position = holder.getAdapterPosition();
                if (position != RecyclerView.NO_POSITION) {
                    if (mDataList.get(position).getTimeDisplayState() == ON_STATE) {
                        mMessageList.get(position).setTimeDisplayState(DEFAULT_STATE);
                    } else {
                        mMessageList.get(position).setTimeDisplayState(ON_STATE);
                    }
                    notifyItemChanged(position);
                }
            }

        });
        return holder;
    } 
}

请注意,当单击recyclerview行时,我使用它来显示和隐藏recyclerview项目中的上次更新时间。在我的ViewHolder.bind()中检查timeDisplayState,并相应地显示时间textview。

请注意,通过在ViewHolder的构造函数中创建OnCLickListener可以实现相同的结果,这是因为在OnCreateViewHolder()中调用了该构造函数。这样做的好处是可以使适配器做得更少并将决策/细节委托给持有者,从而允许使适配器更简单(更少的代码行)并且更通用。否定的是你不能只创建一个OnClickListener来不加区别地附加到所有持有者类型;你必须在每个viewholder类的构造函数中创建监听器。虽然不是一个重大问题。

© www.soinside.com 2019 - 2024. All rights reserved.