Skip to content

Optimize RecyclerView performance after returning from NoteEditActivity (scroll position, lag, unnecessary re-rendering) #208

Description

@amarradi

📝 Description

When returning from NoteEditActivity to MainActivity, the app currently reloads the entire note list, resulting in a noticeable jump (especially in GridView layout) and UI lag before the list becomes usable. This negatively impacts user experience, particularly with many notes or when the ReleaseNote header is visible.

🔍 Observed behavior

  • After closing NoteEditActivity, noteViewModel.loadNotes() is called in MainActivity.
  • This triggers the observer for notesLiveData, which in turn calls noteAdapter.updateNotes(...).
  • That invokes DiffUtil.calculateDiff(...) internally, causing the entire adapter content to be recalculated.
  • Then, recyclerView.scrollToPosition(...) is called to restore the previous scroll position.
  • This entire sequence happens on the UI thread, leading to stuttering and visible jumping.
  • With StaggeredGridLayoutManager, layouting is especially expensive.

⚠️ Root cause

  • loadNotes() always loads the full note list, even when only one note was edited or created.
  • This causes a full rebind in the adapter, triggering layout invalidation.
  • Scroll restoration occurs too early, before RecyclerView has finished layouting.
  • The ReleaseNote header is reset on every return, even if unchanged → triggers layout recalculation.

✅ Expected behavior

  • Only update the changed or new note in the list (e.g., via updateSingleNote(...)).
  • Avoid full list reload unless necessary.
  • Restore scroll position only after RecyclerView layout is complete, ideally without expensive ViewTreeObserver.
  • Only update the ReleaseNote header if its content actually changed.

✅ Suggested solution (for next version)

1. Add updateSingleNote(Note note) method in NoteViewModel

  • Replaces the matching note in the notesLiveData list.
  • Triggers a minimal update via setValue().

2. In NoteEditActivity

  • Add the updated note to the result Intent:
    intent.putExtra("updated_note", note);
    setResult(RESULT_OK, intent);

3. In MainActivity

  • Handle the updated note in ActivityResultLauncher:
    if (result.getResultCode() == RESULT_OK) {
        Intent data = result.getData();
        if (data != null && data.hasExtra("updated_note")) {
            Note updated = data.getParcelableExtra("updated_note");
            noteViewModel.updateSingleNote(updated);
            scrollAfterLoadToPosition = lastScrollPosition;
        }
    }
  • Do not call loadNotes() after every return unless absolutely necessary (e.g., delete or category switch).

4. Restore scroll position using recyclerView.post(...), only if scrollAfterLoadToPosition >= 0.

5. Prevent unnecessary ReleaseNote updates

if (!releaseNote.equals(noteAdapter.getReleaseNote())) {
    noteAdapter.setReleaseNoteHeader(releaseNote);
}

🛠 To-do list

  • Add updateSingleNote(...) method to NoteViewModel
  • Return updated_note from NoteEditActivity using Intent.putExtra
  • In MainActivity, avoid loadNotes() after return and use the new update method instead
  • Move scroll restoration logic into the notesLiveData observer
  • Add check to avoid resetting the ReleaseNote header if it's unchanged
  • Optionally, also improve onReleaseNoteClosed() to avoid unnecessary reloads

🕓 Timing

This optimization is scheduled for a future release to allow a clean and well-tested implementation.

🧩 Affected components

  • MainActivity.java
  • NoteViewModel.java
  • NoteEditActivity.java
  • NoteAdapter.java

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions