ScrollView overflowing its FrameLayout parent

2019-07-22 11:31发布

问题:

I have a complicated LinearLayout (vertical) with a FrameLayout above some other stuff (a TextView and an EditText box). The FrameLayout contains two things: a) a map and b) a ScrollView containing a semi-transparent LinearLayout that 'overlays' the map. The ScrollView is initially GONE, but I render it visible programmatically. I also programmatically add Views into the LinearLayout embedded in the ScrollView.

The FrameLayout dimensions are set using onMeasure() so that the height and width are equal. This make the FrameLayout take up the top portion of the device screen while the TextView and EditText fit below.

public static class MyFrameLayout extends FrameLayout {

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

    @Override
    public void onMeasure(int widthSpec, int heightSpec) {
        super.onMeasure(widthSpec, heightSpec);
        int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
        setMeasuredDimension(size, size);
    }
}

Everything actually works great in the beginning. However, when I add enough Views to the LinearLayout embedded in the ScrollView the ScrollView will eventually extend behind the TextView below the FrameLayout. In other words, the ScrollView overflows its FrameLayout parent, the bottom of which is then hidden. As I continue to add Views to the LinearLayout embedded in the ScrollView the ScrollView will eventually start scrolling. It appears as though this happens once the ScrollView is the height of the device screen.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activityRoot"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#323232"
    tools:context=".MainActivity" >

    <FrameLayout
        class="com.myapp.main.MyFrameLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#070707" >

        <com.myapp.main.MapView
            android:id="@+id/map"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <ScrollView
            android:id="@+id/overlayScrollView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:layout_gravity="left"
            android:scrollbars="vertical"
            android:fillViewport="false"
            android:visibility="gone" >
            <LinearLayout
                android:id="@+id/overlayLinearLayout"
                android:orientation="vertical"
                android:layout_width="100dp"
                android:layout_height="wrap_content"                
                android:padding="1dp"       
                android:background="#AA000000" >
            </LinearLayout>
        </ScrollView>

    </FrameLayout>

    <TextView
        android:id="@+id/tvMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:textColor="#FFFFFF"
        android:textSize="14sp" />

    <EditText android:id="@+id/entry"
        android:background="@drawable/entry_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:lines="1"
        android:singleLine="true"
        android:textSize="16sp"
        android:textColor="#ffffff"
        android:hint="@string/entry" />

</LinearLayout>

What I would like to happen is for the ScrollView to start scrolling as soon as it's the height of its FrameLayout parent. Put another way, I'd like for the ScrollView to remain contained within its FrameLayout parent.

Any idea what I'm doing wrong here?

回答1:

Update:

The new ConstraintLayout library gives you fine-grained control of how your views are sized in relation to the parent and to other views. It also supports aspect ratios, so you can (for instance) force a view to always be square.

Original response:

The problem is how you are measuring your special FrameLayout. When you call super.onMeasure(), this view and all of its children will measure themselves according to the measure specs given. After that, you constrain the dimensions of this view, but none of its children are aware of this; only this view and its parent are aware of this. Its children have measured themselves thinking the size of this view is not constrained by your extra call to setMeasuredDimension().

Calling super.onMeasure() again will force its children to measure themselves again, thereby informing them of the new size.

@Override
public void onMeasure(int widthSpec, int heightSpec) {
    super.onMeasure(widthSpec, heightSpec);
    int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
    int spec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
    super.onMeasure(spec, spec);
}

Note this will cause this view and all its children to measure twice as many times with each measure pass, so it might not be the most performant approach, but it will accomplish what you want.