Jeff Lockhart
Jeff Lockhart

Reputation: 5579

Have view wrap content, but match parent if space is available

I have two views within a LinearLayout, both of which should wrap_content such that they are minimally large enough to display their content. The LinearLayout view group should wrap_content of the two child groups such that it is just large enough to display the content of both child views.

But after this, if one of the two child views is larger, the other child view should expand to match parent to fill the remaining space available to it. The content of the two child views is dynamic and is unknown which will be larger at runtime.

The two child views are a TextView and a Spinner. The Spinner should fill any remaining space in the LinearLayout width. But if I change the Spinner layout_width to match_parent and the TextView is not large enough, the Spinner will truncate its contents.

Basically I need a way to choose the maximum width between wrap_content and match_parent.

This is the layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parentView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="-2dp"
        android:layout_marginBottom="-2dp"
        android:paddingStart="4dp"
        android:paddingEnd="4dp"
        android:textSize="12sp"
        android:textColor="?android:textColorHint"
        tools:text="Label" />

    <Spinner
        android:id="@+id/spinner"
        style="@style/Widget.AppCompat.Spinner.Underlined"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
<!-- or android:layout_width="match_parent"? need max of both... -->

</LinearLayout>

Upvotes: 7

Views: 6122

Answers (5)

Jeff Lockhart
Jeff Lockhart

Reputation: 5579

Update: Ben P's answer accomplishes this while still using a LinearLayout.

After further testing different solutions, turns out a better solution is to not use LinearLayout as the parent view group. For whatever reason, a FrameLayout provides the desired behavior of wrapping the child view's content, even when the child view is set to match_parent, such that the max of the two sizes is used. LinearLayout does not provide this same behavior.

But then the problem is how to get the two child views to stack vertically. This can be achieved in the FrameLayout with a combination of layout_gravity and a Space view. For some odd reason, at least in my use case of using this layout as an item in a FlexboxLayout parent, setting the FrameLayout height explicitly won't work to size its height. Using wrap_content with a Space view set to the required height does work.

This is a layout that works, without requiring any additional code:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parentView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <!-- setting FrameLayout layout_height doesn't work for some reason -->
    <Space
        android:layout_width="0dp"
        android:layout_height="56dp" />

    <TextView
    android:id="@+id/label"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="top|start"
    android:layout_marginTop="-2dp"
    android:layout_marginBottom="-2dp"
    android:paddingStart="4dp"
    android:paddingEnd="4dp"
    android:textSize="12sp"
    android:textColor="?android:textColorHint"
    tools:text="Label" />

    <Spinner
        android:id="@+id/spinner"
        style="@style/Widget.AppCompat.Spinner.Underlined"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom" />

</FrameLayout>

Upvotes: 0

Ben P.
Ben P.

Reputation: 54194

There's a trick you can use to accomplish this with LinearLayout and no extra views: use wrap_content on the parent LinearLayout and match_parent for both of the children. As long as the LinearLayout doesn't have any children with fixed sizes, this combination will make the overall parent as wide as its widest child.

<LinearLayout
    android:layout_width="wrap_content"
    ...>

    <TextView
        android:layout_width="match_parent"
        .../>

    <Spinner
        android:layout_width="match_parent"
        .../>

</LinearLayout>

enter image description here

enter image description here

(I've added a background color to the LinearLayout to make it easier to see how it's sizing itself.)

Upvotes: 7

Jeff Lockhart
Jeff Lockhart

Reputation: 5579

Update: see my other answer for a better solution.

I wish there was a more elegant solution, but the way I've achieved this is by adding an additional hidden duplicate Spinner view used just for sizing the parent LinearLayout to achieve the max of either wrap_content or match_parent, depending on which is larger.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/parentView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="-2dp"
        android:layout_marginBottom="-2dp"
        android:paddingStart="4dp"
        android:paddingEnd="4dp"
        android:textSize="12sp"
        android:textColor="?android:textColorHint"
        tools:text="Label" />

    <Spinner
        android:id="@+id/spinner"
        style="@style/Widget.AppCompat.Spinner.Underlined"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <!-- visible view width set to match_parent -->

    <Spinner
        android:id="@+id/spinnerHiddenSizer"
        style="@style/Widget.AppCompat.Spinner.Underlined"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:visibility="invisible" />
    <!-- invisible view width set to wrap_content -->
    <!-- also set height to 0dp so it takes up no vertical space -->
    <!-- in code, set this Spinner's adapter to the same as -->
    <!-- the visible spinner so that it's sized the same -->

</LinearLayout>

This way, if wrap_content would cause the Spinner's width to be larger than match_parent, the hidden view will cause the LinearLayout to size to this larger size and the visible match_parent width will expand to this width.

Upvotes: 0

medyas
medyas

Reputation: 1286

try setting the layout weight android:layout_weight. here is a definition from SO: https://stackoverflow.com/a/3996104/8738574. set the weight of the spinner to be bigger to what the textView is, and the width to 0 (zero).

TextView:

android:layout_weight="1"
android:layout_width="0px"

Spinner:

android:layout_weight="2"
android:layout_width="0px"

Upvotes: 0

Dmytro Ivanov
Dmytro Ivanov

Reputation: 1310

The problem that your LinearLayout width is wrap_content. It means children will never fill full width, only if content inside will change width.

Just change line inside LinearLayout. From:

android:layout_width="wrap_content"

To

android:layout_width="match_parent"

Upvotes: 0

Related Questions