Status Update
Comments
il...@google.com <il...@google.com> #2
I'm marking this as "won't fix" as there's nothing Compose here can do, as this is a platform quirk that has been adjusted to be more predictable in recent API versions.
The crux of the issue here is the fitSystemWindows
in the extra View
, like you mentioned, and a platform change in API 30.
Prior to API 30, window insets were dispatched through the view hierarchy in a non-intuitive way: Rather than being a full breadth-first traversal, they would be dispatched in preorder. If the insets were consumed at any point (like if you use fitSystemWindows
), then the preorder would prevent insets from being dispatched to the rest of the view hierarchy. This is very unintuitive: It meant that sibling views (or views deeper in the tree) could stop later views from receiving the dispatch of insets.
In API 30, this behavior was changed to be more intuitive: Now, insets are dispatched in a top-down manner, so they can't be consumed in this weird, cross-tree way.
There's a couple solutions here:
- Avoid
fitSystemWindows
from views if you're handling insets manually - Override
dispatchApplyWindowInsets
in the view groups above where you havefitSystemWindows
defined, and manually perform the new, non-broken dispatching behavior. You can see a comparison of the two methods here:https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/ViewGroup.java;l=7341-7371;drc=6411c81462e3594c38a1be5d7c27d67294139ab8
sc...@gmail.com <sc...@gmail.com> #3
Using composable version 1.2.1, I could fix it using:
override fun onCreate(bundle: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(bundle)
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q){
window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
}
}
Then statusBarsPadding works as expected on Api 29
il...@google.com <il...@google.com> #4
sc...@gmail.com <sc...@gmail.com> #5
sc...@gmail.com <sc...@gmail.com> #6
(update project
il...@google.com <il...@google.com> #7
recycleChildrenOnDetach
causes the LinearLayoutManager
to remove all of its views when it is detached (as you'd expect given the name). However, the scroll position is computed based on which views are visible, so having removed all of its Views, there is no Views available to compute the scroll position to save.
Why this causes an issue in this particular case is that when you replace your MainFragment
, you don't add any animations, which means that the FragmentManager immediately calls removeView()
to remove the Fragment from the view hierarchy. This happens before the child fragment saves its view state. Therefore the child RecyclerView
is detached and LinearLayoutManager
throws out its views (and hence, scroll position) before the state is saved.
Therefore you can work around this problem by adding an animation to your FragmentTransaction
:
supportFragmentManager.beginTransaction()
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out,
android.R.anim.fade_in, android.R.anim.fade_out)
.replace(R.id.root_container, MainFragment(), tag)
.addToBackStack(tag)
.commit()
Which means that the Fragment won't be removed before the state is saved on the child fragments. This issue is actually already being addressed in
Of course, you could also LinearLayoutManager
saves its scroll position before throwing out its views, which would also fix the issue and make it resilient to either ordering of operations.
Description
Version used: 1.2.0
Devices/Android versions reproduced on: Any
Subject: reordering performDestroyView after remove views from Nested Fragment
Scenario:
1) add MainFragment1 in MainActivity. in ParentFragment add ChildFragment1 with RecyclerView.
2) replace MainFragment1 to MainFragment2 in MainActivity. in ChildFragment1 saveState RecyclerView call after remove all views and doesn't save scroll position