RecyclerView.onBindViewHolder called only once
Asked Answered
E

9

36

After many hours of researching I am finally consulting official help.

I have a RecyclerView.Adapter and RecyclerView.ViewHolders that worked perfectly. But for some reasons I do not understand, RecyclerView.Adapter.onBindViewHolder is not called properly.

    private class AttendeeAdapter extends RecyclerView.Adapter<AttendeeHolder> {
    /*FIELDS*/
    private List<Attendee> mAttendeeList;

    /*CONSTRUCTORS*/
    public AttendeeAdapter(List<Attendee> attendees) {
        mAttendeeList = attendees;
        //Log.i(TAG, "AttendeeAdapter size: " + getItemCount());
    }

Based on the Log message (the item count as the size of the list as expected), I assume the AttendeeAdapter was properly instantiated.

So I expect onBindViewHolder(VH, int) method would be called as many times as the size of the List but it is not. The method is called only ONCE!

    /*METHODS*/
    @Override
    public AttendeeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
        View itemView = layoutInflater.inflate(R.layout.list_attendee, parent, false);
        return new AttendeeHolder(itemView);
    }

    @Override
    public void onBindViewHolder(AttendeeHolder holder, int position) {
        Attendee attendee = mAttendeeList.get(position);
        holder.bindAttendee(attendee, position);

        Log.i(TAG, "Binding ViewHolder #" + position);
        /* Binding ViewHolder #0 and that's it */
    }

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

My AttendeeHolder (extending RecyclerView.ViewHolder) goes as the following:

    private class AttendeeHolder extends RecyclerView.ViewHolder {
    /*FIELDS*/
    private EditText mAttendeeNameEditText;
    private Attendee mAttendee;

    /*CONSTRUCTOR*/
    public AttendeeHolder(View itemView) {
        super(itemView);
        mAttendeeNameEditText = (EditText) itemView.findViewById(R.id.edit_text_list_item);
        mAmountEditTextList = new ArrayList<>(eventMaxCount);
      }

    /*METHODS*/
    public void bindAttendee(Attendee attendee, final int position) {
        mAttendee = attendee;
        String attendeeName = mAttendee.getName();

        // Set the name to the EditText if a name has already been set
        if (attendeeName != null) {
            mAttendeeNameEditText.setText(attendeeName);
        }
    }
}

and implemented in the main code as

List<Attendee> attendees = AttendeeLab.get().getAttendeeList();
     mAttendeeAdapter = new AttendeeAdapter(attendees);
     mAmountRecyclerView.setAdapter(mAttendeeAdapter);

I guess the code would work (I think I have not made any change) but the gradle dependencies might not be properly set. That was where I tried modifying recyclerview-v7:23.3.0 to recyclerview-v7:23.1.0 or whatever (None of them worked).

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile 'com.android.support:support-v4:23.3.0'
compile 'com.android.support:recyclerview-v7:23.1.2'
}

Any help or comment would be appreciated. I wish I would be saying good bye to the headache after several hours from now.

Eurystheus answered 25/4, 2016 at 12:4 Comment(3)
Not sure if it this means anything - but how do you access your AttendeeHolder, since it has private accessand it is not located in your AttendeeAdapter ..? Also, use the same version for the support packages in your build.gradle.Purine
The ViewHolder and the Adapter were inner classes in the main code therefore accessible.Eurystheus
You solved the problem! RecyclerViews are working properly using the same (old) version for the support packages, i.e. v7:23.1.2. I am sorry that I have poor understanding on how Gradle works, but is it supposed to work that way? What if I insist on using RecyclerViews with the new recyclerview-v7:23.3.0 libraries?Eurystheus
V
113

The problem is not in your code. Make sure you set layout_height to wrap_content of RecyclerView child item.

Vannie answered 2/5, 2016 at 22:27 Comment(8)
I agree the code works properly. I checked child items' layout_height attributes and found that they are all set to wrap_content. I am pretty sure the problem is about the libraries.Eurystheus
Yes, wrap_content doesn't occupy the entire screen as match_parent does. This way onBind is called other times moreChandler
What do you mean with child item?Mireillemireles
child item mean Row layoutGrader
Thank you! I've wasted so much time on this!Divulgate
That's helpful !Payton
As a stupid person I am, I forgot to wrap_content for my first view type. So other view types get never created since they were not visible. A simple scroll would have been help me.Lineation
@Eurystheus Here are many comments verify the credibility of this answer. If this answer also helps you, please accept it.Roof
A
15

Take care of the onBindViewHolder behaviour in the RecyclerView cycles. RecyclerView.ViewHolder position 0 itemView

android:layout_height="match_parent"

occupies the current displayed screen. Scroll and onBindViewHolder should be triggered but make sure getItemCount was set appropriately.

Solution:

android:layout_height="wrap_content"

Solution with ConstraintLayout as inflated itemView:

<?xml version="1.0" encoding="utf-8"?>    
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"><!-- IMPORTANT -->
    <!-- ... -->
</android.support.constraint.ConstraintLayout>
Amylaceous answered 31/12, 2017 at 11:9 Comment(0)
L
3

In a case I came across, was when my recyclerview was scrollable only horizontally, not vertically.

Lakes answered 22/7, 2019 at 10:45 Comment(0)
P
1

Use the same versions for all your support libraries:

dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile 'com.android.support:support-v4:23.3.0'
compile 'com.android.support:recyclerview-v7:23.3.0' //<<< here
}

Also, the RecyclerView should be added with the 'com.android.support:design:23.3.0' package - the overwriting of the classor whatever magic Gradle has done in the build process with this "duplicate" package may have caused your problem.

Purine answered 25/4, 2016 at 12:48 Comment(1)
No, it doesn't work. onBindViewHolder was called only once. Actually, the last line of the code had been like what you suggested but I thought I should stick to the library that I used to use so I changed as I posted. Cleaning and rebuilding the project does not change the result.Eurystheus
W
1

Check the code where you are setting up your adapter in Activity/Fragment in mine case there was an orientation issue.

activityAddBusinessBinding.rvImageList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)

Wahkuna answered 8/4, 2022 at 8:9 Comment(0)
A
0

For people who have this problem more recently and are running the beta versions of SDK 28 of support library change your target SDK to 27 and use the 27.1.1 version of the support libraries (most recent versions at time of writing).

I was struggling with this problem on the 28.0.0-alpha3 libraries, changed the version to 27.1.1 in my app.gradle and the problem was solved.

implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:support-vector-drawable:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'
Aesthetic answered 19/7, 2018 at 23:5 Comment(0)
B
0

Also check if you set the height of your RecyclerView itself to android:layout_height="wrap_content" OR android:layout_height="match_parent". In both cases, it will adapt the height of a single item and appears to be just one, but you can actually scroll the list. Instead, set the height of the RecyclerView manually.

Boschbok answered 25/3, 2019 at 17:15 Comment(0)
O
0

In my case I had to override getItemId(). When I added different items to an adapter, they had the same id. And onBindViewHolder stopped to call when scrolling. So, I changed from

override fun getItemId(position: Int): Long = list[position].id.toLong()

to

override fun getItemId(position: Int): Long = position.toLong()

The adapter also contains these settings:

override fun getItemCount(): Int = list.size

override fun getItemViewType(position: Int): Int = list[position].viewType

this.setHasStableIds(true)

It would be better to use a collection with unique ids.

Otic answered 17/1, 2020 at 10:40 Comment(0)
T
0

In my case the recyclerview's single item layout's root layout(constraint layout) height was set to match_parent instead of 'wrap_content', changed it to 'wrap_content' and problem is fixed

<androidx.constraintlayout.widget.ConstraintLayout 
android:layout_width="match_parent"
android:layout_height="wrap_content">

<androidx.cardview.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:ignore="MissingConstraints">

    <TextView
        android:id="@+id/tv_city_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:gravity="center"
        android:text="@{location.name}"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:typeface="serif" />

</androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

Topper answered 10/2, 2021 at 15:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.