I want to implement a chat window. That is, the user writes on the bottom of the window and new text scrolls existing text upwards. To do so, I've created a ScrollView with a LinearLayout inside. New EditText views are added to the LinearLayout starting from the bottom, according to the gravity layout:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:layout_gravity="bottom"
android:fillViewport="true"
tools:context=".Chat">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="bottom"
android:layout_gravity="bottom"
android:layout_alignParentBottom="true"/>
</ScrollView>
This works well, but when I want to see the "hidden text" that is up screen and I scroll it up, I'm already at the end of the scroll and the hidden text has disappeared. Instead, the ScrollView allows me to scroll down and new blank space has been added to the bottom of the view.
Just to test the opposite direction, I change the lines:
android:gravity="bottom"
android:layout_gravity="bottom"
to
android:gravity="top"
android:layout_gravity="top"
for both the ScrollView and the LinearLayout and also add the new EditText views with index 0, instead of the default at the end:
linearLayout.addView(editText, 0);
And the Chat Window works perfectly well on the opposite direction up to down. It can be scrolled down to the first line entered. Nothing is lost.
So my question is what has to be added to the first way to make the scroll keep the contents of the LinearLayout and don't lose any text?
Ok, I finally discovered what was going on by looking at the Android sources.
android:layout_gravity="top"
must be set in the LinearLayout.
Explanation:
The child positioning is being calculated at onLayout() of the ScrollView, precisely in the parent's onLayout(), in class FrameView. There, if the child (that is, the LinearLayout) has a layout_gravity of bottom, because it is bigger than the ScrollView, the top border is outside the screen, with negative positioning. Therefore the list is cut at the top. Top border position for the LinearLayout must be 0, and this is achieved by setting the layout_gravity to top.