1

I have a RecyclerView with a Horizontal LinerLayout. It displays numbers from 10 to 1, that is used to rate something.

When I select 10 and scroll back to 1 and select 1. I have to update the UI to remove selection on 10 and update selection on 1. But, when I use findViewHolderForAdapterPosition() to remove the selection on 10 it gives me a NullPointerException

I am getting the position in the ViewHolder with getAdapterPosition().

Then, I use that position to get the ViewHolder by calling findViewHolderForAdapterPosition() on my recycler view object and update the UI to remove the selection from 10.

vh = (RatingRecyclerAdapter.ViewHolder)
                mRecycler.findViewHolderForAdapterPosition(previousPosition);
vh.textRating.setBackgroundResource(R.drawable.rating_background_selected_orange);;

With some tests, I found out when I try to do the same thing without scrolling it works fine. However, only when I am scrolling it gives me a NullPointerException

How do I fix this?

As requested here is some important code from Adapter class.

@Override
public void onBindViewHolder(RatingRecyclerAdapter.ViewHolder holder, int position) {
        String itemText = itemList.get(position);
        holder.textRating.setText(itemText);
}

public class ViewHolder extends RecyclerView.ViewHolder {

        TextView textRating;

        public ViewHolder(View itemView) {
            super(itemView);
            textRating = (TextView) itemView.findViewById(R.id.text_rating);
            textRating.setOnClickListener(ratingClickListener);
        }

        private final View.OnClickListener ratingClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = getAdapterPosition();
                if (callback != null) {
                    callback.onClickRating(v, position);
                }
            }
        };
    }

Activity Class

@Override
public void onClickRating(View view, int position) {
        RatingRecyclerAdapter.ViewHolder vh;
        int color;

        int previousPosition = mAdapter.getSelectedPosition();  //Get previously clicked postion if any.
        if (previousPosition == Constants.NO_ITEM_SELECTED) {
            // An item was selected first time
            vh = (RatingRecyclerAdapter.ViewHolder)
                    mRecycler.findViewHolderForAdapterPosition(position);
            mAdapter.setSelectedPosition(position);  // Save new item selected position.
            color = Utility.getItemColor(mAdapter.getSelectedRating());
            mAdapter.setSelectedRatingResource(vh, color);
            return;
        }

        if (position == previousPosition) // Same item was selected
            return;

        vh = (RatingRecyclerAdapter.ViewHolder)
                mRecycler.findViewHolderForAdapterPosition(previousPosition);
        color = Utility.getItemColor(mAdapter.getSelectedRating());
        mAdapter.setUnselectedRatingResource(vh, color); // Remove the previous selected item drawables.

        vh = (RatingRecyclerAdapter.ViewHolder)
                mRecycler.findViewHolderForAdapterPosition(position);
        mAdapter.setSelectedPosition(position); // Save new item selected position.
        color = Utility.getItemColor(mAdapter.getSelectedRating());
        mAdapter.setSelectedRatingResource(vh, color); // Set the new selected item drawables. Setting some background to indicate selection.

    }
Saran Sankaran
  • 2,335
  • 2
  • 19
  • 34
  • Can you post full adapter class? – Amad Yus Apr 11 '17 at 04:12
  • 1
    Your problem is look like http://stackoverflow.com/questions/43318274/how-to-trigger-radiobutton-to-checked-if-i-pressed-on-linear-layout/43318463#43318463 – Rahul Sharma Apr 11 '17 at 04:18
  • Maybe this will help you [link](http://stackoverflow.com/questions/32836844/android-recyclerview-findviewholderforadapterposition-returns-null) – kelebro63 Apr 11 '17 at 04:19
  • @AmadYus I have added some code as requested. – Saran Sankaran Apr 11 '17 at 04:38
  • @RahulSharma that is not what I am trying to ask. – Saran Sankaran Apr 11 '17 at 04:39
  • @kelebro63, my code is working fine, except when I am scrolling. What I think is, it is related to something with recycling of views. – Saran Sankaran Apr 11 '17 at 04:39
  • OnScroll is it showing null pointer exception on mLayoutmanager.? – Atif AbbAsi Apr 11 '17 at 05:26
  • The Recyclerview holds only the visible values,that's why you are getting null. – Sachin Varma Apr 11 '17 at 05:27
  • 2
    Your `findViewHolderForAdapterPosition()` is returning `null`, because the requested ViewHolder is already recycled. I think you can set the threshold by calling set `setMaxRecycledViews()` on `RecyclerView.RecycledViewPool`. Anyway, the way you do it (calling getAdapterPosition() in the adapter) is perfectly fine. – Sevastyan Savanyuk Apr 11 '17 at 05:39
  • @AtifAbbAsi no, it returns me a Null value on line `vh = (RatingRecyclerAdapter.ViewHolder) mRecycler.findViewHolderForAdapterPosition(previousPosition)` – Saran Sankaran Apr 11 '17 at 05:47
  • @Sevastyan, can you write an answer on how to use the solution which you are suggesting. I am not very experienced with programming, so I don't understand how to use it. I will try and let you know if it worked. – Saran Sankaran Apr 11 '17 at 05:49
  • @SaranSankaran, I prefer not to give answers to such easy/beginner type of questions for your own good, because it takes all the fun out of figuring out it yourself. Moreover, I think there are several solutions to your problem. I **highly recommend** watchin I/O talk on RecyclerView. Start from 18:00. [I/O presentation](https://www.youtube.com/watch?v=LqBlYJTfLP4&t=1672s) – Sevastyan Savanyuk Apr 11 '17 at 06:14
  • @sevastyan, I resolved it by creating a class and storing everything for that individual item. The drawback is that memory required for it much more than that of my earlier code(one in the question.) I will post a solution after few hours – Saran Sankaran Apr 12 '17 at 13:03

2 Answers2

6

As Sevastyan has written in the comment, the RecyclerView immediately recycles the view as soon as the item is out of the screen. So if we call findViewHolderForAdapterPosition() for a view which is outside the screen we get a null value. (I am not confirming this is the actual case. But, this is what it seems to me.)

So I created a class that stores all the data about an item in the RecyclerView and stored all the colours and value of that item in the class. And when we are populating the view, set the all the colours based on data stored in that class.

PS: I THANK Sevastyan for not giving me the answer directly. But, only giving me the reason for getting that Exception.

Saran Sankaran
  • 2,335
  • 2
  • 19
  • 34
0

If your view is out of the screen, it can be recycled OR cached.

In case it's recycled, you can handle in onViewRecycled() method or setup the view again inside onBind() when the view becomes visible (you can save the state on the object of your list if needed).

In case it's not recycled (onViewRecycled method not called for that position), it's probably cached. You can set the cache size to zero to prevent this state from happening.

recycler.setItemViewCacheSize(0)
G_comp
  • 162
  • 3
  • 12