Android ScrollView gets cut off at the bottom
Asked Answered
W

5

18

I am trying to programmatically add form fields picked from an HTML file to a LinearLayout. I have a next button at the bottom but it keeps getting cut off in the display. I tried it on a tablet and it still doesnt show up.

Here's a screenshot of the app: screenshot

As you can see, the elements are getting rendered but the last one runs off the screen for some reason.

Fragment's XML:

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".dataInput.PropertyInfoFragment"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:paddingLeft="20dp"
    android:paddingRight="20dp">

    <ScrollView
        android:fillViewport="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

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

            </LinearLayout>
            <Button
                android:id="@+id/nextButton"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="@string/next"
                android:background="@color/colorPrimary"
                android:textColor="@android:color/white"/>
        </LinearLayout>

    </ScrollView>

</FrameLayout>

Calling Activity's XML:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 
    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:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".dataInput.DataInputActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/AppTheme.PopupOverlay">

        </android.support.v7.widget.Toolbar>

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_media_play" />

</android.support.design.widget.CoordinatorLayout>

I am calling a method formInflator that I made in the fragment's onCreateView and passing the LinearLayout from the fragment and an Elements object (from Jsoup library) which contains all the Elements that I want to put inside the LinearLayout:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    view = inflater.inflate(R.layout.fragment_property_info, container, false);
    nextButton = (Button) view.findViewById(R.id.nextButton);
    nextButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            onButtonPressed();
        }
    });

    helpers.formInflator((LinearLayout) view.findViewById(R.id.linear_layout_property_info), generator.propertyTextElements);
    return view;

}

Here's the method formInflator:

public void formInflator(LinearLayout parentLayout, Elements formElements) {
    TextInputLayout index = null;
    for(Element textField : formElements) {
        TextInputEditText editText = new TextInputEditText(context);
        editText.setId(View.generateViewId());
        editText.setHint(textField.id());
        editText.setText(textField.text());
        LinearLayout.LayoutParams editTextParams = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        editText.setLayoutParams(editTextParams);

        TextInputLayout textInputLayout = new TextInputLayout(context);
        textInputLayout.setId(View.generateViewId());
        textInputLayout.setTag(textField.id());
        RelativeLayout.LayoutParams textInputLayoutParams = new RelativeLayout.LayoutParams(
                RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        if (index == null)
            index = textInputLayout;
        else
            textInputLayoutParams.addRule(RelativeLayout.BELOW, index.getId());

        textInputLayout.setLayoutParams(textInputLayoutParams);
        textInputLayout.addView(editText, editTextParams);

        parentLayout.addView(textInputLayout, textInputLayoutParams);
        index = textInputLayout;
    }
}

Any idea what I am doing wrong?

Winther answered 29/7, 2016 at 16:23 Comment(5)
if it's a scrollview surely you can just scroll up to see the rest of "cut-off" content? what is the intended behaviour you're trying to achieve?Makalu
@Makalu I think I didnt word my question properly. The issue is that there's supposed to be a button after all the EditText views. However the ScrollView does not scroll all the way to the bottom. What I've shown in the screenshot is the furthest it goes.Winther
I have a feeling its due to the action bar. I am facing the same issue. I am using a tabbed activity, and inside the tabs I have EditTexts. Initially, the bottom gets cut off just like OP's. But once i activate a EditText and the soft keyboard comes up, the action bar vanishes(this is another issue I can't seem to solve), but the ScrollView works perfectly, even after the keyboard is removed (action bar is still missing).Wattage
Solution here: https://mcmap.net/q/669355/-android-scrollview-won-39-t-scroll-all-the-way-to-the-bottom-of-my-linearlayoutMonro
If your (Nested)ScrollView is inside a ConstraintLayout, on your scrollview you may need to set android:layout_height="0dp" (along with app:layout_constraintTop_toBottomOf and app:layout_constraintBottom_toTopOf) - as described here: https://mcmap.net/q/336893/-scrollview-inside-constraint-layout-does-not-scroll-to-the-bottom-of-the-parent-constraint.Monro
K
17

Try the Nested ScrollView. It did wonders for me.

<androidx.core.widget.NestedScrollView
    -----
    android:layout_width="match_parent"
android:layout_height="match_parent">

LinearLayout<
-----
</LinearLayout>
</androidx.core.widget.NestedScrollView>
Klaraklarika answered 25/4, 2020 at 6:26 Comment(1)
Exact solution of this problem for API 21 - 29.Gorgon
A
13

Change your ScrollView layout_height to match_parent. It's the child that gets wrap_content.

Aleman answered 29/7, 2016 at 16:47 Comment(2)
Thanks, that definitely helps! However it doesnt solve the problem completely. Now it scrolls to the bottom of the last EditText view but the Button still gets cut off. I tried moving the Button above the second LinearLayout and that results in the last EditText being cutoff againWinther
In formInflator, you are trying to set RelativeLayout layout params for the TextInputLayout added to parentLayout. But parentLayout is a LinearLayout, not a RelativeLayout, so that can't possibly be doing what you expect.Aleman
L
10

The most close solution that I have ever found is adding a ghost view... If the ScrollView is eating part of the last element, give it a cookie.

Ex:

<View
    android:layout_width="match_parent"
    android:layout_height="25dp"/>
Leibowitz answered 9/5, 2017 at 17:3 Comment(2)
You can simply add bottom padding to the content inside your scrollview.Gybe
Nobody should use this suggestions. The scrollview will still be cutted off at the bottom even if the content is shown...Aggravate
D
5

Here's how i figured out how to do it without padding. The scrollview gets perfectly to the bottom on every screen size and orientation I have tried. Notice the height is 0dp and there is a constraint to the bottom of parent.

<ScrollView
    android:id="@+id/scrollView"
    style="@android:style/Widget.DeviceDefault.Light.ScrollView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/LLHeaders">

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

        <My dynamically generated LinearLayout horizontal rows go here>

    </LinearLayout>
</ScrollView>
Decompound answered 14/5, 2018 at 15:36 Comment(0)
C
0

Add your action bar to the activity. Android will then better recognise the view port and add the missing toolbar height on the bottom, when you scroll.

AppCompatActivity

Toolbar toolbar = getActivity().findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// if needed...
getSupportActionBar().setDisplayShowTitleEnabled(false);
Caryl answered 13/9, 2017 at 9:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.