8

I've implemented SearchView + Recyclerview using this on github. GITHUB
My next step is to get the item on selected part on the recyclerview.
Then I saw some code getting the child on recyclerview.
The code is working when the getChildAt(index) =0.
But when i put on index=12 or greater than that.
The program crashed.

mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.addOnItemTouchListener(
                new RecyclerItemClickListener(getContext(), new RecyclerItemClickListener.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        final int valueThisIteration = position;
                        mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                            @Override
                            public void onGlobalLayout() {
                                TextView textViewDrawerTitle = (TextView) mRecyclerView.getChildAt(valueThisIteration).findViewById(R.id.tvText);
                                textViewDrawerTitle.setText("Checked");
                                mRecyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                            }
                        });

                    }
                })
        );

And im getting this error.

11-04 09:36:02.818 1629-1629/? E/AndroidRuntime: FATAL EXCEPTION: main
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime: Process: com.thesis.juandirection.juandirectionfinale, PID: 1629
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at com.thesis.juandirection.juandirectionfinale.fragments.FragmentSearch$1$1.onGlobalLayout(FragmentSearch.java:94)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:815)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1867)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1054)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5779)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.view.Choreographer.doCallbacks(Choreographer.java:580)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.view.Choreographer.doFrame(Choreographer.java:550)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.os.Handler.handleCallback(Handler.java:739)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:95)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:135)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:5221)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Native Method)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:372)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
11-04 09:36:02.818 1629-1629/? E/AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Charles Galvez
  • 1,100
  • 5
  • 19
  • 41
  • Well as the StackTrace points out you're getting a null value from `mRecyclerView.getChildAt(valueThisIteration)` during one of the iterations. Are you simply trying to handle item clicks of the RecyclerView? There are better ways to do it, if that's what you're after. – AdamMc331 Nov 04 '15 at 14:43
  • Thats one. But I also want to get the selected item on recycler view. Store it in a variable and so on. – Charles Galvez Nov 04 '15 at 14:45
  • Okay. I will write up an answer, I believe I understand. – AdamMc331 Nov 04 '15 at 14:45
  • Not sure if getChildAt will return the row views, anyway why do you want to do that, if you want the data, you should have your model containing it, i you want the entire row view, just recreate it... – Nanoc Nov 04 '15 at 14:45
  • Just set an onItemClickListener on the recyclerView it will also give you the selected index... – Nanoc Nov 04 '15 at 14:46
  • I'am able to get the index. But my problem is I want to get the data. How can i get the data from the model? – Charles Galvez Nov 04 '15 at 14:49
  • @CharlesGalvez Are you storing your data inside a `List` or an `Array` object inside the Adapter class? If that is the case, I believe the approach in my answer will work just fine. – AdamMc331 Nov 04 '15 at 14:53

1 Answers1

42

For handling item clicks in a RecyclerView, I recommend you move your logic into the ViewHolder of your Adapter. You can have your ViewHolder implement View.OnClickListener, and override the onClick() method to preform an action. If your action depends on the item clicked, you can reference it using getAdapterPosition(). The code will look something like this:

public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
   public MyViewHolder(View view) {
      super(view);

      view.setOnClickListener(this);
   }

   @Override
   public void onClick(View view) {
      // Get the item clicked
      // For this example, I'm assuming your data source is of type `List<MyObject>`
      MyObject myObject = mDataSource.get(getAdapterPosition());
      // Then you can do any actions on it, for example: 
      myObject.setChecked();
   }
}

Clearly the logic that happens inside onClick will change to your example, but I hope this sets you on the right track. For another sample of this, as well as a bit of an explanation for the benefits of handling click logic inside the ViewHolder, check out a blog post that I wrote comparing RecyclerView and ListView (look for the section labeled "More explicit click listeners").

Homayoon Ahmadi
  • 1,181
  • 1
  • 12
  • 24
AdamMc331
  • 16,492
  • 10
  • 71
  • 133
  • Thank you for this! It worked. But my ripple effect gone. :/ – Charles Galvez Nov 04 '15 at 15:04
  • I Fixed it now. THanks so much for this! – Charles Galvez Nov 04 '15 at 15:07
  • @CharlesGalvez I won't lie, I haven't been able to work that out myself either. In the apps that I've used this I just let it go since the ripple is just a cosmetic benefit. I will look into it though and if I can figure it out I'll come back and let you know. – AdamMc331 Nov 04 '15 at 15:07
  • 1
    Oh. What did you change? – AdamMc331 Nov 04 '15 at 15:07
  • 1
    its on my fault.I set the the view.setOnClickListener(this) on my textView. It's now working fine. Thanks. – Charles Galvez Nov 04 '15 at 15:14
  • @CharlesGalvez simple mistake! Glad to help. – AdamMc331 Nov 04 '15 at 15:15
  • How can i Add startActivity on viewHolder? im implementing when the user clicked on the item then it will transfer to other activity the print the item. I'm able to get the item but cannot use intent inside the class. – Charles Galvez Nov 04 '15 at 16:10
  • There's a good chance you keep a `Context` object at the class level of your Adapter, yes? If you do, inside onClick you can do something like `mContext.startActivity(new Intent(mContext, NewActivity.class));` – AdamMc331 Nov 04 '15 at 16:20
  • @McAdam331Hey I have a similar problem, I want to start a new activity when an item is clicked, for which I need a `Context` object. Now my adapter does have a `Context` object but since my `ViewHolder` is static, it can't access the `Context` object from the adapter. Any way to get around this? – akshayt23 Jan 21 '16 at 08:02
  • you can get context from view, like view.getContext(); – bastami82 Jun 23 '17 at 06:59
  • 3
    where do you get mDataSource from? – eskalera Oct 19 '17 at 17:39
  • @AdamMc331 My ViewHolder class is declared inside my Activity. I am using a [FirebaseRecyclerAdapter](https://github.com/firebase/FirebaseUI-Android/blob/master/database/src/main/java/com/firebase/ui/database/FirebaseRecyclerAdapter.java) so I don't see how I can access the adapter from the ViewHolder. I could have a bindModel method as you do in your blog post, but is that a best practice? I thought model should be bound to view inside onBindViewHolder. – eskalera Oct 20 '17 at 10:08
  • Well I call the bind method from `onBindViewHolder` in the example. The code is the same, but as a personal preference I like to put it in my ViewHolder. @eskalera – AdamMc331 Oct 20 '17 at 14:01
  • @AdamMc331 how to add click event on different-2 views elements of a single holder – Aman Jain Jan 02 '18 at 12:24
  • @AmanJain Instead of calling `view.setOnClickListener()` set it on the views you want to handle. For example, `textView.setOnClick..` and `imageView.setOnClick...` – AdamMc331 Jan 03 '18 at 00:12
  • @AdamMc331 i have used butterknife to bind OnClick on each elements of holder, is this a good approach? @OnClick(R.id.like) public void like(TextView view) { this.holderItemClickAction.like(getAdapterPosition()); #this is interface which i call once click hapen } – Aman Jain Jan 03 '18 at 09:49
  • 3
    `getAdapterPosition()` may return `NO_POSITION` (-1), in which case the code above will crash. What should we do in that case? – Minas Mina Feb 02 '19 at 14:15