Status Update
Comments
il...@google.com <il...@google.com>
jb...@google.com <jb...@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
se...@gmail.com <se...@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
se...@gmail.com <se...@gmail.com> #5
Wow, that is very informative! I guess I had missed the part where the parent Fragment
’s inflation info is encoded in it's onCreateView(…)
’s LayoutInflater
. So FragmentContainerView
's crash was actually trying to tell me that I was doing something wrong and that the Activity
's FragmentManager
was being used...
It sounds like a good rule of thumb is to add support for passing in a LayoutInflater
to a custom View
if it contains a FragmentContainerView
and might get nested in a parent Fragment
.
Thank you so much!
Description
**Version used**: `1.2.1`
**Devices/Android versions reproduced on**: All
Nesting a `FragmentContainerView` in a custom `View`'s layout crashes when that custom `View` is returned in the `onCreateView()` of a **parent** `Fragment`:
```
Caused by: android.view.InflateException: Binary XML file line #23: Error inflating class androidx.fragment.app.FragmentContainerView
Caused by: java.lang.IllegalStateException: FragmentManager is already executing transactions
at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1778)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1814)
at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:303)
at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:166)
at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:51)
at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:356)
at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:335)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:780)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
at androidx.databinding.DataBindingUtil.inflate(DataBindingUtil.java:126)
at androidx.databinding.ViewDataBinding.inflateInternal(ViewDataBinding.java:1368)
at goober.goofy.com.playground.databinding.CustomViewWithFragmentBinding.inflate(CustomViewWithFragmentBinding.java:54)
at goober.goofy.com.playground.databinding.CustomViewWithFragmentBinding.inflate(CustomViewWithFragmentBinding.java:40)
at goober.goofy.com.playground.ui.main.CustomViewWithFragment.<init>(CustomViewWithFragment.kt:15)
at goober.goofy.com.playground.ui.main.CustomViewWithFragment.<init>(CustomViewWithFragment.kt:11)
at goober.goofy.com.playground.ui.main.MainFragment.onCreateView(MainFragment.kt:22)
```
**However, things work when** inflating the xml containing `FragmentContainerView` _directly_ (bypassing the custom `View` shell) in the parent `Fragment`'s `onCreateView()`.
Please see the attached sample project. Class of interest is `MainFragment`. Two blocks are present inside `onCreateView()`: one that works and one that fails. The failing block is currently uncommented in the sample.
Not sure if this is a library bug or an implementation issue. Looking for workarounds or suggestions.
**Sample project**: `fragmentcontainerview-crash-sample.zip`
Thanks