-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
fix(card-template-editor): prevent button flickering with 4+ card types #19977
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
My immediate thought when I read this:
...was "okay, so if I have very large templates (and some people have entire javascript bundles in there practically, along with like tailwind CSS...) and I have 100 of them (or, get ridiculous, 1000...) am I now open to an OutOfMemory error? Stated more generically: we are dealing with user-provided boundless input (card templates), and using a bounded system (device memory) Indeed, the API docs are prescriptive on this point:
Is there some way to put a reasonable cap on the number of templates we keep in memory, just in case? Say, 10? That will still leave us with flicker though - it seems that there will be an unavoidable flicker with our current code if we accept the limits of device memory and have a limit on in-memory fragments. I think the correct solution is to figure out where in the lifecycle things have gone wrong leading to a temporarily incorrect state + flicker, and to use some of the provided callback APIs to register interest in lifecycle stages where we can update the UI as needed at the correct time so we have correct UI state and no flicker ViewPager2 has some scrolling callbacks, the adapter inside of it has a few callbacks In that sense, the current state where only 4 templates causes a flicker is in fact a good test case for the real fix Then perhaps it is interesting to keep more templates off screen but that is just a performance / smoothness fix - a separate thing - vs a UI correctness / anti-flicker fix |
Thank you Mike for that detailed feedback, i'also had the same in my mind but this was initial plan, got busy so i pushed it right there. Now I investigated this thoroughly- logcatThe old fragment's Lifecycle approaches i tried ():
umm i feel |
That's most strange - especially since you have a log in there where you know it is the "wrong" fragment 🤔 how did you know it was the wrong fragment? What's the code path that does onCreateMenu ? Seems like a bug, basically - like that onCreateMenu call should either not happen (by fixing whatever bug in our code causes it to happen or subclassing framework code and no-oping it somehow if possible, or ... some other idea), or if you were able to know it was the wrong fragment (somehow?) could maybe stop it from creating it's menu as well If there is really no way to do a clean solution where the menu is just working correctly by showing the correct fragment, then having it work well for a "reasonable" number of fragments at least does seem fine. In that context, 7 is probably pretty conservative, could likely handle 10 or 20 without worry if we're being honest - just not unbounded. |
Added debug logging that includes the fragment's ordinal
Each CardTemplateFragment registers its own MenuProvider in setupMenu(): requireActivity().addMenuProvider(menuProvider, viewLifecycleOwner, Lifecycle.State.RESUMED)When the menu is invalidated (via
Tried this! Added a check in onCreateMenu: val currentPosition = (requireActivity() as CardTemplateEditor).viewPager.currentItem
if (arguments?.getInt(CARD_INDEX) != currentPosition) return But Robolectric tests failed - shadowOf(activity).clickMenuItem() returns false because the menu isn't properly set up when we skip inflation. The framework expects onCreateMenu to actually inflate the menu.
The
Conclude: |
mikehardy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No fault at all of your own, but this is so lame 😆
I relent, "works for me"
Leaving to second reviewer a taste check for:
"how many should we keep to cover a few standard deviations of likely template counts?"
7 total seems fine? 10 or 15 would probably cover 99.9999% of cases. Worth increasing slightly? 🤷
Set offscreenPageLimit=7 to keep fragments alive during ViewPager2 transitions. Both old and new fragments' MenuProviders fire during tab switches, causing flicker when fragments are destroyed/recreated. Fixes ankidroid#18555
BrayanDSO
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
7 is generally considered a lucky number
Purpose / Description
Template editor buttons (BottomNavigationView with Front/Back/Styling options) flicker and sometimes disappear when switching between card types in note types with 4+ cards.
Fixes
Approach
Set
ViewPager2.offscreenPageLimit = 3to prevent fragment destruction during tab transitions. The flicker occurs because both old and new fragments' MenuProviders are active simultaneously during ViewPager2 transitions, and lifecycle-based fixes don't work since both fragments remain in RESUMED state. Bounded at 3 to balance flicker prevention with memory usage, keeping up to 7 fragments alive..How Has This Been Tested?
Tested on -
Learning (optional, can help others)
ViewPager2 uses RecyclerView internally with a default caching strategy that only keeps adjacent pages
https://developer.android.com/reference/androidx/viewpager2/widget/ViewPager2#setOffscreenPageLimit(int)
Checklist
Please, go through these checks before submitting the PR.