4

I'm trying to use a recycler view and handle on click event. I've read various methods of handling onClick event on a recycler view item like :

  1. Define the click listener inside the view holder class itself.
  2. Define the click listener in onCreateViewHolder().
  3. Define an interface and then go from there (seems like too much work).

So my first question is which option is better? I'm currently using the first method and if defining the click listener in the view holder class itself is the way to go, then how do I use the context from the adapter as the view holder class is static.

Basically, I want to have a static view holder and on click event, open a new Activity for which I need the context.

UPDATE : Adding adapter and ViewHolder code.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private Context mContext;
    private List<Job> jobs;

    public MyAdapter(Context context, List<Job> jobs) {
        mContext = context;
        this.jobs = jobs;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View itemLayoutView = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.list_item, viewGroup, false);

        itemLayoutView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(mContext, MyActivity.class);
                mContext.startActivity(intent);
            }
        });

        return new ViewHolder(itemLayoutView);
    }

    @Override
    public void onBindViewHolder(WorkExperienceAdapter.ViewHolder viewHolder, int i) {
            //bindViewHolder code
        }
    }

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

    public static class ViewHolder extends RecyclerView.ViewHolder {

        @InjectView(R.id.current)
        TextView mCurrent;

        public ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.inject(this, itemView);
        }
    }
}
Keivan Esbati
  • 3,376
  • 1
  • 22
  • 36
akshayt23
  • 2,793
  • 3
  • 21
  • 29
  • Your first question, which approach is better, would yield primarily opinion based answers, which are unfit for SO format. – Shamas S Jan 21 '16 at 07:42
  • Yes but I wanted to know if there is any performance difference when it comes to comparing the first two methods. – akshayt23 Jan 21 '16 at 07:51

3 Answers3

9

In the view holder constructor we get the object of View class. You can use that object to get the context like:

class Holder extends RecyclerView.ViewHolder {

    public Holder(View itemView) {
    super(itemView);
    Context context = itemView.getContext();
   }

}
Bhawna Raheja
  • 619
  • 4
  • 11
  • Hi, I actually tried this method but it doesn't work because `itemView.getContext()` doesn't return an activity context, and hence it's not possible to start another activity using it. – akshayt23 Jan 21 '16 at 07:49
  • What about setting listener in onBindViewHolder() method? – Bhawna Raheja Jan 21 '16 at 07:50
  • @user2558050 `itemView.getContext()` returns a `Context`, so use it for calling `startActivity()` – pskink Jan 21 '16 at 07:52
  • Yes I could actually do that, but I think it should be done on `onCreateViewHolder()` rather than `onBindViewHolder()` because I think registering a new listener every time a view is reused in `onBindViewHolder()` could affect performance, but I'm not sure. – akshayt23 Jan 21 '16 at 07:54
  • @pskink I tried using the `Context` object returned by `itemView.getContext()` to start an activity, but got a runtime exception saying `android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?` – akshayt23 Jan 21 '16 at 07:55
  • @user2558050 how are you creating the `View`s inside your `ViewHolder`? just use a right `Context` when creating them – pskink Jan 21 '16 at 08:07
  • @pskink I don't think I understand your comment properly. I create my views using the following code : `TextView tv = (TextView) itemView.findViewById(R.id.textview);`. As I've mentioned, `itemView.getContext()` doesn't return an activity context, hence I cannot use it to start an activity, it throws an exception. – akshayt23 Jan 21 '16 at 08:15
  • no, `findViewById` does not create any `View`, my question is: how do you create your `View` passed to `ViewHolder` constructor? – pskink Jan 21 '16 at 08:18
  • @pskink Ohh, I do that in the `onCreateViewHolder()` method and then pass the view to the `ViewHolder` constructor. So I assume adding the `onClickListener` in `onCreateViewHolder()` itself should be the same as adding it in the `ViewHolder` class? – akshayt23 Jan 21 '16 at 08:22
  • @user2558050 i have no idea what you are asking about... so how do you create your `View` inside `onCreateViewHolder`? what `Context` are you using there? – pskink Jan 21 '16 at 08:24
  • @user2558050 pskink is saying right. When you create a view holder, you pass a view in the constructor of view holder. So set listener on that view in OnCreateViewHolder() method. – Bhawna Raheja Jan 21 '16 at 08:25
  • @BhawnaRaheja Yes I was saying the exact same thing, that I could set listener on the view in `OnCreateViewHolder()` before passing it to the `ViewHolder` constructor. – akshayt23 Jan 21 '16 at 09:14
  • @pskink In the `onCreateViewHolder()` method, I get a `ViewGroup` object as a parameter, and I use `viewgroup.getContext()` to get a `Context` object which I use to inflate the view. – akshayt23 Jan 21 '16 at 09:17
  • @user2558050 post your `Adapter` and `ViewHolder` code – pskink Jan 21 '16 at 09:21
  • @user2558050 what does `Log.d(TAG, "ctx0: " + viewgroup.getContext().getClass())` show? and `Log.d(TAG, "ctx1: " + itemView.getContext().getClass())`? – pskink Jan 21 '16 at 09:38
  • They both return `.BaseApplication`. – akshayt23 Jan 21 '16 at 10:35
  • @user2558050 that explains everything, how are you adding your `RecyclerView` then? what is yourRecyclerView.getContext().getClass() ? – pskink Jan 21 '16 at 10:59
  • @pskink Well I dont have a reference to the RecyclerView inside the Adapter, but I checked in the Activity and it also returned the exact same thing. – akshayt23 Jan 21 '16 at 11:58
  • @user2558050 how are you adding your `RecyclerView` then? – pskink Jan 21 '16 at 17:22
0

Try using the reference of the Activity.

ActivityOne.this.startActivity(intent);

If that doesn't work, then know that startActivity is a method of any Context.

class MessageViewHolderOfFriend extends RecyclerView.ViewHolder {

    private final Context context;

    public  MessageViewHolderOfFriend(View v) {
        super(v);
        context = v.getContext();

        v.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(context,NewActivityToRun.class);
                context.startActivity(intent);
            }
        });

    }

REF:

Community
  • 1
  • 1
Diego Venâncio
  • 5,698
  • 2
  • 49
  • 68
0

The best approach that you can use for the same is by creating a BaseRecyclerView abstract class, and including some method to get context in there.

For example, you base class would look something like this

abstract class BaseRecyclerViewAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

private lateinit var context: Context

override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
    super.onAttachedToRecyclerView(recyclerView)

    context = recyclerView.context      
}

    protected fun getContext() = context
}

Now your Adapter class would not be extending RecyclerView.Adapter, but would extend BaseRecyclerViewAdapter class. This would look something like

class MyAdapter(): BaseRecyclerViewAdapter() {
    
}

You can now use context in MyAdapter simply by using getContext() which is a protected function inside the base class.

Whenever your RecyclerView is created, the base class assigns the context of the recyclerView to the variable named context. This context can now be accessed by using the getContext() method in the base class.

Priyansh Kedia
  • 171
  • 1
  • 6