4

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>
Jeff Lockhart
  • 5,379
  • 4
  • 36
  • 51

5 Answers5

6

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.)

Ben P.
  • 52,661
  • 6
  • 95
  • 123
  • This does do the trick! Odd how this behavior is for `LinearLayout`, differing from `FrameLayout`. Thanks for the tip! – Jeff Lockhart Jan 02 '19 at 21:21
  • I ended up finding this out on my own through trial and error, but can confirm that does the trick. I banked my head against the wall with ConstraintLayouts but nothing ever worked right. – Oded Feb 17 '21 at 06:56
0

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"
Dmytro Ivanov
  • 1,260
  • 1
  • 8
  • 10
  • This is not what I'm looking for. Making this change will make the `LinearLayout` expand to fill its parent. The `LinearLayout` needs to wrap its children's content. – Jeff Lockhart Dec 30 '18 at 16:24
0

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"
medyas
  • 1,166
  • 13
  • 21
  • This won't work, as the `layout_weight` works in the direction the `LinearLayout` stacks its child views. In the case of my vertical `LinearLayout`, it would allow children to expand their **height** to fill remaining space. I need them to expand their **width**. – Jeff Lockhart Dec 30 '18 at 16:28
  • okay, so you need to determine which view is the bigger and set the other to it, try getting their width at run-time and check which one is bigger and set their widths – medyas Dec 30 '18 at 20:15
  • I am trying to find a way to do this in the layout directly, without requiring code to do it. The view sizes are only known after layout occurs, so this would require potentially multiple layouts to achieve programmatically. – Jeff Lockhart Dec 31 '18 at 19:45
0

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.

Jeff Lockhart
  • 5,379
  • 4
  • 36
  • 51
0

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>
Jeff Lockhart
  • 5,379
  • 4
  • 36
  • 51
  • I believe you can get what you want by using a LinearLayout and without the extra `Space` view, though I can't say for sure that it will work in your FlexboxLayout use case. Check out my answer below and let me know. – Ben P. Jan 02 '19 at 18:02