I have a screen with a transparent status bar and a ScrollView containing a hero image and then some other elements. The image should be drawn under the status bar. Everything looks fine on the first look but I've noticed that the last view in the ScrollView is clipped. It looks like the ScrollView height expands below the limits of the screen height. I can see the over scroll effect starting somewhere below the screen. When I use android:fitsSystemWindows="true"
on the ScrollView it solves the problem but then I am not drawing under the status bar.
Some relevant code:
Fragment.onCreate
WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false)
app theme
<item name="android:statusBarColor">@android:color/transparent</item>
I'm not sure if pasting the XML layout makes sense, but in pseudocode:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView />
<TextView />
<TextView />
<TextView />
<TextView />
</LinearLayout>
</ScrollView>
CodePudding user response:
It is in fact a big pain to get the drawing below the status bar working as expected. I myself use the below lines to get this done, however using flags is marked deprecated now.
private fun drawBelowStatusBar(window: Window) {
if (!BitFlagsUtil.isFlagSet(
source = window.decorView.systemUiVisibility,
flag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
)
) {
window.decorView.systemUiVisibility = BitFlagsUtil.setFlag(
source = window.decorView.systemUiVisibility,
flag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
)
}
}
and the util class consists of these 3 methods.
object BitFlagsUtil {
fun isFlagSet(source: Int, flag: Int): Boolean {
return source and flag == flag
}
fun setFlag(source: Int, flag: Int): Int {
return (source or flag)
}
fun unsetFlag(source: Int, flag: Int): Int {
return (source and flag.inv())
}}
PS. android:fitsSystemWindows
gets tricky with CoordinatorLayout and CollapsingToolbarLayout
CodePudding user response:
WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false)
From documentation: Using this piece of code ensures that your app goes edge-to-edge, that is, laid out using the entire width and height of the display.
But that can make the system bottom navigation bar overlaps with the activity obscuring the bottom part of it which in your case the bottom of the ScorllView
Solution:
The documentation offers a solution to that using insets:
You can address overlaps by reacting to insets, which specify which parts of the screen intersect with system UI such as the navigation bar or the status bar. Intersecting can mean simply being displayed above the content, but it can also inform your app about system gestures, too.
This requires to know the top root ViewGroup
of the activity layoutin order to use the appropriate LayoutParams
. This is ScrollView
in your case:
val root = findViewById<ScrollView>(R.id.root) // Cast that to the id of your root layout (ScrollView)
ViewCompat.setOnApplyWindowInsetsListener(root) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.layoutParams = (view.layoutParams as FrameLayout.LayoutParams).apply {
leftMargin = insets.left
bottomMargin = insets.bottom // to draw above the navigation bar
rightMargin = insets.right
}
// Return CONSUMED if you don't want want the window insets to keep being
// passed down to descendant views.
WindowInsetsCompat.CONSUMED
}