0

I have two RecylerViews in a LinearLayout with vertical orientation inside a ScrollView. Both RecyclerViews want to display a list of 6 strings in TextViews. The arrangement is such that at the start, the TextViews of just the first RecyclerView show up and upon scrolling, those of the second RecyclerView show up.

My expectation is that since none of the TextViews of the second RecyclerView is visible, they won't be created, and once they come in view, the onBindViewHolder will be called to reuse existing ViewHolders. However, all of the individual ViewHolders are created (onCreateView called). Am I doing something wrong? Because if this is expected, there is no point of setting a common ViewPool, is there?

Activity Code

public class MainActivity extends AppCompatActivity {
    private List<String> names = Arrays.asList(
        "Name 1", "Name 2", "Name 3", "Name 4", "Name 5", "Name 6"
    );

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

        RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();

        RecyclerView recycler1 = (RecyclerView) findViewById(R.id.recycler1);
        RecyclerView recycler2 = (RecyclerView) findViewById(R.id.recycler2);

        recycler1.setLayoutManager(new LinearLayoutManager(this));
        recycler2.setLayoutManager(new LinearLayoutManager(this));

        recycler1.setRecycledViewPool(viewPool);
        recycler2.setRecycledViewPool(viewPool);

        recycler1.setNestedScrollingEnabled(false);
        recycler2.setNestedScrollingEnabled(false);

        recycler1.setAdapter(new SampleAdapter(new ArrayList<>(names), 1));
        recycler2.setAdapter(new SampleAdapter(new ArrayList<>(names), 2));
    }

    private class SampleAdapter extends RecyclerView.Adapter {
        private final List<String> mSampleList;
        private final int mRecyclerId;

        SampleAdapter(List<String> sampleList, int recyclerId) {
            mSampleList = sampleList;
            mRecyclerId = recyclerId;
        }

        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            Log.i("TESTING", "creating a new view for parent Id: " + mRecyclerId);
            TextView textView = (TextView) getLayoutInflater().inflate(R.layout.text_view_name, null);
            return new SampleViewHolder(textView);
        }

        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            Log.i("TESTING", "binding existing view for parent Id: " + mRecyclerId + " for position: " +
                position);
            ((SampleViewHolder)holder).bind(mSampleList.get(position));
        }

        public int getItemCount() {
            return mSampleList.size();
        }
    }

    private class SampleViewHolder extends RecyclerView.ViewHolder {
        private TextView mText;
        public SampleViewHolder(TextView itemView) {
            super(itemView);
            mText = itemView;
        }

        public void bind(String s) {
            mText.setText(s);
        }
    }
}

Layout

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

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

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimary"/>

    </LinearLayout>
</ScrollView>

TextView

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/name_text_view"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    android:paddingTop="40dp"
    android:paddingRight="40dp"
    android:paddingBottom="40dp"
    android:paddingLeft="40dp"
    android:textColor="@color/white"
    android:gravity="center">

</TextView>
amitsaurav
  • 531
  • 1
  • 5
  • 18

1 Answers1

0

First off, using RecyclerView inside a ScrollView is never a good idea. A ScrollView will layout its child, in your case a LinearLayout, which will also layout its children: The 2 RecyclerViews.

I will start from bottom up: Both RecyclerView get to be as tall as their parent, or less, since they are told to wrap_content. They both get layouted, and are within the LinearLayout, which will have the height of both RecyclerViews. In case the LinearLayout is bigger than the screen, the ScrollView will make everything scrollable. You now have a tall layout that shows the full content and is scrollable.

Things to notice: A scrollview does not recycle any views. If a view is off screen it might not get drawn, but the view itself does not "act" upon this information, for all the view knows it is currently displayed to the user. Basically both RecyclerViews are properly layouted to their full height within the LinearLayout, which will stretch to the height needed. Al the RecyclerViews know is that they are visible—and need to have all their children bound. This is your "problem".

there is no point of setting a common ViewPool, is there?

Since both RecyclerViews are "visible" there is no recycling and no reuse. A RecyclerView that doesn't scroll because it is inside a ScrollView will not recycle its views. You are correct, there is no recycling going on, and the shared ViewPool is useless.


With the layout you describe you should get rid of the ScrollView altogether and use a single RecyclerView to properly scroll and recycle the views. Whatever you need the ScrollView for, you can accomplish the same with a RecyclerView. ScrollView is for static layouts, RecyclerView for lists.

Shared ViewPools are intended to be used if you have multiple Fragments or things like ViewPagers, where you have a lot of the same Views over different screens.

David Medenjak
  • 33,993
  • 14
  • 106
  • 134
  • Thanks David, for the detailed explanation. I understand what you're saying but this is a simplified version of my problem. In reality, I have multiple different types of "things" to show which are aggregated in Lists. Imagine, a list of lists. My thinking was to have 1 `RecyclerView` per list, which would give me a list of `RecyclerViews` for the outer list of lists. Then I would use a single ViewPool among all the RecyclerViews to efficiently use memory. How can I then still allow scrolling, with the list of `RecyclerViews` in a LinearLayout? – amitsaurav Feb 27 '17 at 17:46
  • @amitsaurav Please don't. If you have lists of lists you can concatenate all of them within a single list and still have one adapter handle them, different view types for different items, or you create some main adapter wrapping and delegating to child adapters, one for each list. There is a lot of possibilities, but wrapping recyclerviews within a scrollview will _not_ scale well. – David Medenjak Feb 27 '17 at 18:02
  • Alright, the takeway then is, use my own custom adapter that flattens the list of list so that I end up using a single recycler view for the collection. Thanks for all the pointers. I will still keep the question open to see if there is a good way to have a list of RecyclerViews with a shared ViewPool on the screen. – amitsaurav Feb 27 '17 at 19:05