5

Here is what I would like my ScrollView to look like:

  • The maximum size is defined with the layout_weight (so that other items below the ScrollView can be displayed properly)
  • If the content is smaller than that maximum size, then it just behaves as with layout_height="wrap_content"

Here is what I currently have:

<ScrollView
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:measureAllChildren="true"
            android:fillViewport="false"
            >

I don't think the measureAllChildren really does anything at all...

If I add android:layout_weight, the size will always be what I would like the maximum to be. Without it, it just extends more than it should...

I don't mind extending the ScrollView class to change the behavior of onMeasure if I need to...?

PS: If that makes a differences, I am trying to get this working from Froyo onward.

Matthieu
  • 16,103
  • 10
  • 59
  • 86

5 Answers5

7

I ended up writing my own class, extending ScrollView

Since you ask...here is the code. Probably not the cleanest but it does what I want.

Note that it expects the layout_weight to be set when the view is created and you should not set the weigthSum in the parent LinearLayout or you'll get funny things (since the weight of this one changes from the original value to 0 depending on the size of the content of the ScrollView)

First, in the layout file, the view is declared like this:

<com.matthieu.widget.ShrinkingScrollView
    android:id="@+id/scroll"
    android:scrollbars="vertical"
    android:layout_height="0dp"
    android:layout_width="fill_parent"
    android:layout_weight="4"
    android:background="#cc0000"
    >
    <TextView
        android:id="@+id/in_scroll_view"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:background="#0000bb"
        />
</com.matthieu.widget.ShrinkingScrollView>

Then the code for the widget:

import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.widget.ScrollView;

public class ShrinkingScrollView extends ScrollView {
    private float original_weight=-1;
    public ShrinkingScrollView(Context context) {
        super(context);
    }

    public ShrinkingScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ShrinkingScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
        float previous_weight = params.weight;

        if (original_weight == -1)
            original_weight = params.weight;

        if ((getChildCount()>0) && (getVisibility()!=GONE)) {
            super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED));
            int overall_height = getChildAt(0).getMeasuredHeight();
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if (getMeasuredHeight() >= overall_height) {
                if (previous_weight != 0) {
                    params.weight=0;
                    params.height = overall_height;
                    setLayoutParams(params);
                    post(new Runnable() {
                        public void run() {
                            requestLayout();
                        }
                    });
                }

                setMeasuredDimension(getMeasuredWidth(),overall_height);
            }
            else if (previous_weight == 0) {
                params.weight = original_weight;
                params.height = 0;
                setLayoutParams(params);
                post(new Runnable() {
                    public void run() {
                        requestLayout();
                    }
                });
            }
        }
        else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}
Matthieu
  • 16,103
  • 10
  • 59
  • 86
0

Here another solution using sizes instead of weights. I've extended ScrollView and added code to implement this feature:

https://gist.github.com/JMPergar/439aaa3249fa184c7c0c

I hope that be useful.

JMPergar
  • 1,906
  • 1
  • 19
  • 21
0

JMpergar answer seems right but its not worked until I changed the onMeasure method like below.

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        try {
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            if (maxHeight != WITHOUT_MAX_HEIGHT_VALUE
                    && heightSize > maxHeight) {
                heightSize = maxHeight;
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.AT_MOST);
                getLayoutParams().height = heightSize;

            } else {
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.AT_MOST);
                setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec)
                        , getDefaultSize(this.getSuggestedMinimumHeight(), heightMeasureSpec));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
Hardik
  • 17,179
  • 2
  • 35
  • 40
0

you can just wrap your ScrollView into another ConstraintLayout, then constrain the height (the line app:layout_constrainedHeight="true"):

<!-- another constraint layout to keep ScrollView shrinking -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constrainedHeight="true"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:id="@+id/content_layout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
Francis
  • 6,788
  • 5
  • 47
  • 64
0

If I understand your requirement correctly, the way I have done something like this is to place the scrollview along with anything else you want to display below it into a Relative layout. Then, in your layout file place the item on the bottom of the screen in the file first (with android:layout_alignParentBottom="true"), then put the scroll view and place it above the first item using something like:android:layout_above="@id/firstItem". Like this in which I put an image view on the bottom with a scroll view above it taking the remaining space:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/searchByNameScreen"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_gravity="center_horizontal"
    android:layout_marginTop="1dip"
    android:gravity="center_horizontal"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/bottomImage"
        android:layout_width="fill_parent"
        android:layout_height="75dip"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:paddingBottom="15dip"
        android:paddingLeft="10dip"
        android:paddingRight="10dip"
        android:paddingTop="7dip"
        android:scaleType="fitXY"
        android:src="@drawable/android_450x50_moreinfo" />

    <ScrollView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/ScrollView01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/bottomImage"
        android:layout_marginBottom="3dip"
        android:layout_marginLeft="10dip"
        android:layout_marginRight="10dip"
        android:layout_marginTop="1dip"
        android:scrollbars="none" >

        <LinearLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/aboutCopyLayout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/aboutTopLayer"
            android:layout_centerHorizontal="true"
            android:layout_gravity="center_horizontal"
            android:background="@drawable/rounded_background"
            android:gravity="center"
            android:padding="5dip" >

            <!-- android:background="@drawable/rounded_background" -->

            <TextView
                android:id="@+id/aboutCopyText"
                style="@style/ResortNameExtraLine"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:gravity="center_horizontal"
                android:text="@string/aboutCopy" />
        </LinearLayout>
    </ScrollView>
</RelativeLayout>
Kaediil
  • 5,465
  • 2
  • 21
  • 20
  • Need to try it... I have a few questions though... does not look like "orientation" is right for a RelativeLayout ? I don't want what's below the ScrollView to be at the bottom of the screen... will it work if I "fill" the layout from top to bottom also ? I think this could be a good alternative, although I cannot use any weight as in the LinearLayout – Matthieu Feb 27 '12 at 16:49