Search code examples
androidandroid-layoutstatusbarandroid-scrollview

Top of ScrollView hidden behind status bar


I have a FrameLayout which contains ImageView for background and LinearLayout within ScrollView to display all of the Views I need. Simplified code is displayed below and is used for my Fragment

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"     
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/ink"
        android:scaleType="center"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/linear_layout_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="vertical">
        </LinearLayout>

   </ScrollView>
</FrameLayout>

This fragment is put within FrameLayout of my activity_main.xml which looks like this

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    >
</FrameLayout>

When sum of item heights become bigger than height of the screen ScrollView kicks in and I can scroll all the items I have within LinearLayout like expected.

The problem is that when I scroll to top of the LinearLayout, the uppermost View is hidden behind status bar like in this picture:

The behavior I'm looking for is this:

The top of the view is below the status bar and fully visible.

How can I accomplish the result shown in the second image?

My styles.xml is as follows

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
</resources>

Solution

  • Here are the rules to follow:

    • Don't use android:layout_gravity attribute on children of ScrollView.
      • It produces unwanted behavior, see comments for details.
    • Don't use android:layout_height="match_parent" on children of ScrollView
      • The child doesn't want to be as big as the screen (scroll view viewport). The whole point is the child has a precise height or wants to expand to fit its own contents.
      • While match_parent may behave as wrap_content for children of scroll views, philosophically it makes no sense here.

    If you need to center the content on the whole screen and allow scrolling when it doesn't fit, use this setup:

    <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true">
        <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center_vertical"
                android:orientation="vertical">
            <!-- Etc. -->
        </LinearLayout>
    </ScrollView>
    

    android:fillViewport="true" expands the child if its smaller than the scroll view.

    Notice the missing layout_ prefix in android:gravity.

    • android:gravity tells the LinearLayout how to position its own children inside itself.
    • android:layout_gravity tells whoever the parent is (here ScrollView) how to position the LinearLayout within the parent.