4

I have a case when I need to have RecyclerView inside NestedScrollView. The problem is that during init methods onCreateViewHolder and onBindViewHolder calls for EVERY item in list (100 items in example).

So there is no recycling views on the screen and in the more complex case I have great problems with performance.

Code of my Activity:

public class MyActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final RecyclerView recyclerView = findViewById(R.id.recycler_view);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(new MyListAdapter());
}
}

Code of my .xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/refresh"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:nestedScrollingEnabled="false"/>

</android.support.v4.widget.NestedScrollView>
</RelativeLayout>

Code of my Adapter:

public class MyListAdapter extends RecyclerView.Adapter<MyListAdapter.Holder>{

@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
    final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
    Log.d("TESTING", "onCreate: " + view.hashCode());
    return new Holder(view);
}

@Override
public void onBindViewHolder(Holder holder, int position) {
    Log.d("TESTING", "onBind position: " + position + ", view: " + holder.view.hashCode());
    holder.setContent(position % 2 == 0 ? R.color.colorPrimary : R.color.colorAccent);
}

@Override
public int getItemCount() {
    return 100;
}

class Holder extends RecyclerView.ViewHolder {

    private View view;

    public Holder(View itemView) {
        super(itemView);
        view = itemView;
    }

    public void setContent(int colorRes) {
        view.setBackgroundResource(colorRes);
    }
}
}

And during init I have this logs:

    onCreate: 147045034
    onBind position: 0, view: 147045034
    onCreate: 235006520
    onBind position: 1, view: 235006520
    onCreate: 16439158
    onBind position: 2, view: 16439158
    onCreate: 84373988
    onBind position: 3, view: 84373988
    onCreate: 146076930
    onBind position: 4, view: 146076930
    onCreate: 12306512
    onBind position: 5, view: 12306512
    onCreate: 167862094
    onBind position: 6, view: 167862094
    onCreate: 220010876

    ... logs for items 7 - 94

    onCreate: 189894540
    onBind position: 95, view: 189894540
    onCreate: 121777898
    onBind position: 96, view: 121777898
    onCreate: 38672504
    onBind position: 97, view: 38672504
    onCreate: 210522038
    onBind position: 98, view: 210522038
    onCreate: 243811364
    onBind position: 99, view: 243811364
Pablo
  • 141
  • 9
  • Have a look at https://stackoverflow.com/questions/37322061/recyclerview-inside-nestedscrollview-onbindviewholder-calling-for-all-getitemcou – ADM Mar 24 '18 at 12:47
  • @ADM I tried to change attrs `android:layout_height` as they said in this answer, but it doen't help ( – Pablo Mar 24 '18 at 13:02

2 Answers2

5

I have had this issue before, and from my findings it's impossible to have this setup and have the RecyclerView actually recycle views.

RecyclerView calculates how many views it has to show by asking its parent view how big it is.
ScrollView asks its children to inflate all its views, so it can calculate its own height, and see how much it should be able to scroll.

This causes conflicts, so it means that the RecyclerView will inflate ALL of its children views, which basically renders the RecyclerView useless.

My fix was to have all elements to be a part of the RecyclerView, and then setup different views for different viewtypes. Thereby eliminating the ScrollView.

Moonbloom
  • 6,529
  • 2
  • 21
  • 36
0

I was able to solve this issue by wrapping the RecyclerView in SEVERAL additional layouts. In my case the reason for the NestedScrollView is to support an AppbarLayout and nested scrolling behavior. I am loading a fragment into the NestedScrollView, which typically contains a recyclerview.

The key here seems to be the ConstraintLayout combined with the FrameLayout.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/parent_nested_scroll"
    android:fillViewport="true"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <LinearLayout
        android:id="@+id/fragmentHolder"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >

            <FrameLayout
                android:layout_width="0dp"
                android:layout_height="0dp"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent">

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recyclerView"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"/>

            </FrameLayout>

        </android.support.constraint.ConstraintLayout>

    </LinearLayout>

</android.support.v4.widget.NestedScrollView>
Shooky
  • 1,099
  • 7
  • 15