(#) RecyclerView Problems !!! ERROR: RecyclerView Problems This is an error. Id : `RecyclerView` Summary : RecyclerView Problems Severity : Error Category : Correctness Platform : Android Vendor : Android Open Source Project Feedback : https://issuetracker.google.com/issues/new?component=192708 Since : 2.0.0 (April 2016) Affects : Kotlin and Java files Editing : This check runs on the fly in the IDE editor Implementation : [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/RecyclerViewDetector.kt) Tests : [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RecyclerViewDetectorTest.kt) `RecyclerView` will **not** call `onBindViewHolder` again when the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined. For this reason, you should **only** use the position parameter while acquiring the related data item inside this method, and should **not** keep a copy of it. If you need the position of an item later on (e.g. in a click listener), use `getAdapterPosition()` which will have the updated adapter position. (##) Example Here is an example of lint warnings produced by this check: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~text src/test/pkg/RecyclerViewTest.java:69:Error: Do not treat position as fixed; only use immediately and call holder.getAdapterPosition() to look it up later [RecyclerView] public void onBindViewHolder(ViewHolder holder, int position) { ------------ src/test/pkg/RecyclerViewTest.java:82:Error: Do not treat position as fixed; only use immediately and call holder.getAdapterPosition() to look it up later [RecyclerView] public void onBindViewHolder(ViewHolder holder, final int position) { ------------------ src/test/pkg/RecyclerViewTest.java:102:Error: Do not treat position as fixed; only use immediately and call holder.getAdapterPosition() to look it up later [RecyclerView] public void onBindViewHolder(ViewHolder holder, final int position) { ------------------ src/test/pkg/RecyclerViewTest.java:111:Error: Do not treat position as fixed; only use immediately and call holder.getAdapterPosition() to look it up later [RecyclerView] public void onBindViewHolder(ViewHolder holder, final int position, List<Object> payloads) { ------------------ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here is the source file referenced above: `src/test/pkg/RecyclerViewTest.java`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~java linenumbers package test.pkg; import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.TextView; import java.util.List; @SuppressWarnings({"ClassNameDiffersFromFileName", "unused"}) public class RecyclerViewTest { // From https://developer.android.com/training/material/lists-cards.html public abstract static class Test1 extends RecyclerView.Adapter { private String[] mDataset; public static class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ViewHolder(TextView v) { super(v); mTextView = v; } } public Test1(String[] myDataset) { mDataset = myDataset; } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.mTextView.setText(mDataset[position]); // OK } } public abstract static class Test2 extends RecyclerView.Adapter { public static class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View v) { super(v); } } @Override public void onBindViewHolder(ViewHolder holder, int position) { // OK } } public abstract static class Test3 extends RecyclerView.Adapter { public static class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View v) { super(v); } } @Override public void onBindViewHolder(ViewHolder holder, final int position) { // OK - final, but not referenced } } public abstract static class Test4 extends RecyclerView.Adapter { private int myCachedPosition; public static class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View v) { super(v); } } @Override public void onBindViewHolder(ViewHolder holder, int position) { myCachedPosition = position; // ERROR: escapes } } public abstract static class Test5 extends RecyclerView.Adapter { public static class ViewHolder extends RecyclerView.ViewHolder { public ViewHolder(View v) { super(v); } } @Override public void onBindViewHolder(ViewHolder holder, final int position) { new Runnable() { @Override public void run() { System.out.println(position); // ERROR: escapes } }.run(); } } // https://code.google.com/p/android/issues/detail?id=172335 public abstract static class Test6 extends RecyclerView.Adapter { List myData; public static class ViewHolder extends RecyclerView.ViewHolder { private View itemView; public ViewHolder(View v) { super(v); } } @Override public void onBindViewHolder(ViewHolder holder, final int position) { holder.itemView.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { myData.get(position); // ERROR } }); } @Override public void onBindViewHolder(ViewHolder holder, final int position, List payloads) { holder.itemView.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { myData.get(position); // ERROR } }); } } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can also visit the [source code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/RecyclerViewDetectorTest.kt) for the unit tests for this check to see additional scenarios. The above example was automatically extracted from the first unit test found for this lint check, `RecyclerViewDetector.testFixedPosition`. To report a problem with this extracted sample, visit https://issuetracker.google.com/issues/new?component=192708. (##) Suppressing You can suppress false positives using one of the following mechanisms: * Using a suppression annotation like this on the enclosing element: ```kt // Kotlin @Suppress("RecyclerView") fun method() { notifyDataSetChanged(...) } ``` or ```java // Java @SuppressWarnings("RecyclerView") void method() { notifyDataSetChanged(...); } ``` * Using a suppression comment like this on the line above: ```kt //noinspection RecyclerView problematicStatement() ``` * Using a special `lint.xml` file in the source tree which turns off the check in that folder and any sub folder. A simple file might look like this: ```xml <?xml version="1.0" encoding="UTF-8"?> <lint> <issue id="RecyclerView" severity="ignore" /> </lint> ``` Instead of `ignore` you can also change the severity here, for example from `error` to `warning`. You can find additional documentation on how to filter issues by path, regular expression and so on [here](https://googlesamples.github.io/android-custom-lint-rules/usage/lintxml.md.html). * In Gradle projects, using the DSL syntax to configure lint. For example, you can use something like ```gradle lintOptions { disable 'RecyclerView' } ``` In Android projects this should be nested inside an `android { }` block. * For manual invocations of `lint`, using the `--ignore` flag: ``` $ lint --ignore RecyclerView ...` ``` * Last, but not least, using baselines, as discussed [here](https://googlesamples.github.io/android-custom-lint-rules/usage/baselines.md.html).