11 November 2015

Android layout with view fixed to bottom


I recently submitted a stack overflow question about a problem I was having. I didn't get the answer I was looking for so eventually I answered it myself.

The concept I wanted to achieve was to have a view stick to the bottom of the screen. Pretty easy in a relative layout with alignParentBottom="true". However the screen was a registration screen, so it had edit text boxes. When you click on an edit text box, the soft keyboard appears. The problem I then had of course was that my bottom view then popped up to the top of the keyboard and obscured most of the screen.



The above diagram goes a little way to representing what I was seeing, with three being the keyboard and two being the view I wanted stuck to the bottom. The overall container being a RelativeLayout with the blue being a ScrollView and the red alignParentBottom="true".

After much head scratching I finally stumbled across a solution, although it was a bit different to what I was expecting.

First I moved the red bottom view into the scrollview and added a stretcher view with a height of zero.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:isScrollContainer="false">

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

            <EditText />
            <EditText />

            <View
                android:id="@+id/stretcher"
                android:layout_width="match_parent"
                android:layout_height="0dp" />

            <TextView
                android:id="@+id/2"
                android:layout_gravity="bottom"
                text="2" />
        </LinearLayout>
    </ScrollView>
</RelativeLayout>


The next step was to add a view tree observer.

    final View mainLayout = getView();
    final View mainContent = getView().findViewById(R.id.content);
    final View stretcherView = getView().findViewById(R.id.stretcher);

    //Main layout uses weight some, so we can't hard code the size of the circles.
    //We must dynamically re-size
    mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (android.os.Build.VERSION.SDK_INT >= 16) {
                mainLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            } else {
                mainLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            }

            //Calculate the desired height of the stretcher view to be the remainder between full screen and content.
            int stretchHeight = mainLayout.getHeight() - mainContent.getHeight();

            //Apply calculated height remainder to stretched view.
            //This enables our bottom box to be pushed to the bottom without obstructing the content when the keyboard appears.
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) stretcherView.getLayoutParams();
            params.height = stretchHeight;
            stretcherView.setLayoutParams(params);
        }
    });


This listener watches the entire screen and then measures the difference between the full screen and the scrollview. It then inflates the stretcher view by this difference. What we're effectively doing is nudging the bottom view to the bottom of the screen. Now when we load the page the bottom view sits nicely on the bottom of the screen, but when the keyboard moves the bottom view stays underneath and appears in the scroll.

Hope this helps somebody else!