0

I have the following code for example.

MainActivity.java

public class MainActivity extends AppCompatActivity {

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

        List<String> names = new ArrayList<>();
        names.add("one");
        names.add("two");
        names.add("three");
        names.add("four");
        names.add("five");
        names.add("six");
        names.add("seven");
        names.add("eight");
        names.add("nine");
        names.add("ten");

        RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
        LinearLayoutManager llm = new LinearLayoutManager(this);
        rv.setLayoutManager(llm);

        RvAdapter adapter = new RvAdapter(names);
        rv.setAdapter(adapter);
    }
}

RvAdapter.java

public class RvAdapter extends RecyclerView.Adapter<RvAdapter.PersonViewHolder> {
    public static final String TAG = RvAdapter.class.getSimpleName();
    List<String> persons;

    RvAdapter(List<String> persons) {
        this.persons = persons;
    }

    @Override
    public RvAdapter.PersonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        return new PersonViewHolder(v);
    }

    @Override
    public void onBindViewHolder(RvAdapter.PersonViewHolder holder, int position) {
        holder.name.setText(persons.get(position));
        Log.d(TAG, "onBindViewHolder: " + position);
    }

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

    public static class PersonViewHolder extends RecyclerView.ViewHolder {
        CardView cv;
        TextView name;

        PersonViewHolder(View itemView) {
            super(itemView);
            cv = (CardView) itemView.findViewById(R.id.cv);
            name = (TextView) itemView.findViewById(R.id.name);

        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="match_parent"
    tools:context="com.example.mrmayhem.myapplication.MainActivity">
            <android.support.v7.widget.RecyclerView
                android:id="@+id/rv"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

            </android.support.v7.widget.RecyclerView>
</RelativeLayout>

item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp">

    <android.support.v7.widget.CardView
        android:id="@+id/cv"
        android:layout_width="match_parent"
        android:layout_height="200dp">

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </android.support.v7.widget.CardView>
</LinearLayout>

It works so good and correct. When I scroll through the list I get successive messages from Adapter one by one in Logs: "onBindViewHolder: " + position. But my problem in next. When i try add some View above the RecyclerView, as shown in the code below. Adapter displays all calls of the method onBindViewHolder simultaneously. Wrap the RecyclerView in activity_main.xml by the next code.

activity_main.xml (with header)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="match_parent"
    tools:context="com.example.mrmayhem.myapplication.MainActivity">

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

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="HEADER" />

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

            </android.support.v7.widget.RecyclerView>
        </LinearLayout>

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

</RelativeLayout>
Vishal Chhodwani
  • 2,567
  • 5
  • 27
  • 40
Mikhail
  • 800
  • 8
  • 21

2 Answers2

2

RecyclerView is a ListView. It will only display the items that you can currently see, reusing parts as you scroll.

Now you wrap the RecyclerView in a LinearLayout within a NestedScrollView, what happens?

A NestedScrollView needs to know the size of the LinearLayout to handle the scrolling.
How does the LinearLayout know how long it should be, without knowing the RecyclerViews total length? It doesn't.

So the recyclerView gets completely inflated, all views at once, all in a long line. No recycling, no reuse. This is the phenomenon you experience.
Now the LinearLayout can properly tell the NestedScrollView it's height (the total height of the TextView and all the items of the RecyclerView) and the NestedScrollView can handle the scrolling.


This is why nested scrolling is always a bad idea. If possible, try to move your TextView as an item into the RecyclerView and get rid of the wrapping NestedScrollview.
e.g. as seen here: Is there an addHeaderView equivalent for RecyclerView?

Another option would be to remove the recyclerview from the scroll view and put it below. The header would not scroll then, though.

Community
  • 1
  • 1
David Medenjak
  • 33,993
  • 14
  • 106
  • 134
0

Unless you have a good reason to have a NestedScrollView in your hierarchy, you could achieve a TextView on top with a RecyclerView below with just this layout code:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.mrmayhem.myapplication.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="HEADER" />

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

    </android.support.v7.widget.RecyclerView>

</LinearLayout>
zsmb13
  • 85,752
  • 11
  • 221
  • 226