27

In short, is it possible to tell a child in a RelativeLayout to always match the height of that RelativeLayout regardless of how other children in that same RelativeLayout behave? In short, that's it. Details below.

What I'm trying to achieve is a ListView row with at least three views, and one of those views would be kind of a stripe at the right side of the list entry (the red view below). The problem I'm having is that there is a TextView at the left side, and depeding on how much text I have, the stripe won't fill the whole layout. This is very clearly explained by images below.

ListView item layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

<View
        android:id="@+id/bottom_line"
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:layout_alignParentBottom="true"
        android:background="#fc0" />

<!-- Why match_parent does not work in view below? -->
<View
        android:id="@+id/stripe"
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:minHeight="50dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:background="#f00" />

<TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_toLeftOf="@id/stripe"
        android:text="This is a very long line, meant to completely break the match_parent property of the box at right"
        style="?android:textAppearanceLarge"/>

</RelativeLayout>

Result:

RelativeLayout with child not filling the whole layout vertically

Setting stripe and root height to match_parent makes no difference. I did it.

Repeating the question, I want the red stripe to always fill the parent vertically. You can see stripe is not aligning to the top of root.

A note: the above example is the simplest, self-contained example I can think of. It's just a ListActivity with an anonymous ArrayAdapter populated by a static String array. The relevant code in onCreate is at most 8 lines, so no worries there. It's really the layout. Besides, I have this working with nested LinearLayouts already, but I'm trying to reduce the depth of my layout structure a bit, if possible. LinearLayout works fine, as expected.

Onik
  • 19,396
  • 14
  • 68
  • 91
davidcesarino
  • 16,160
  • 16
  • 68
  • 109
  • @JD.com RelativeLayouts don't work with weight properties (well, except for the parent of the RelativeLayout itself...). – davidcesarino Aug 18 '13 at 21:15

5 Answers5

35

I had the same requirement and my solution was to align the top of the red stripe to the parent's top, and the bottom of the stripe to the bottom of the text view on the left. The height in this case becomes irrelevant. You can either use wrap_content or match_parent.

sergej shafarenka
  • 20,071
  • 7
  • 67
  • 86
  • I'm accepting your solution because I should have worded my question better. Although your answer fits the question as is, what I really meant was that it should be set regardless of other views (it was implied in the first sentence, but the example was too restricted). For example, if I use a `TextView` as a stripe, that solution won't work if we have the opposite: if the text is smaller vertically than the stripe (in which case the stripe content will be cut out). Again, I should have worded it better (as in: the stripe could be made of other views besides View. Not "regardless" as implied. – davidcesarino Aug 18 '13 at 21:28
  • You are right. In this case RelativeLayout has height="wrap_content" meaning it refers to the height of children. If you define a child with a height="match_parent", then this child refers to the height of its parent. Parent refers to child's height and child refers to parent's one. I believe this is what RelativeLayout cannot handle, at least for now. You need another anchor, and I came to a solution with another child, which is located down below all the others. – sergej shafarenka Aug 18 '13 at 21:35
19

Simply put your main RelativeLayout inside a LinearLayout, then the calculation of child Views height will be correct . I had the same problem and it worked for me.

SMR
  • 6,628
  • 2
  • 35
  • 56
Behzad
  • 207
  • 2
  • 3
6

Try this:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

    <View
        android:id="@+id/stripe"
        android:layout_width="80dp"
        android:layout_height="match_parent"
        android:layout_alignBottom="@id/text"
        android:layout_alignParentRight="true"
        android:layout_alignTop="@id/text"
        android:background="#f00"
        android:minHeight="50dp"/>

    <TextView
        android:id="@+id/text"
        style="?android:textAppearanceLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_toLeftOf="@id/stripe"
        android:text="This is a very long line, meant to completely break the match_parent property of the box at right"/>

    <View
        android:id="@+id/bottom_line"
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:layout_below="@id/text"
        android:background="#fc0"/>

</RelativeLayout>

The align top and bottom of the red view is the same as textview now.

Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
Namenlos
  • 1,615
  • 2
  • 18
  • 24
  • 1
    Sorry, but no. With your solution, the stripe width depends on the text width (toRightof=text), and will eventually be pushed out of the layout if the text reaches the maximum width of the root. Also, if the stripe content is taller than text, the stripe content will be cut out. – davidcesarino Aug 18 '13 at 21:13
  • You are right! I edited my code and now it works correct ;-) (changed "toRightOf" from View to "toLeftOf" from TextView) – Namenlos Aug 18 '13 at 21:21
  • Thanks for your efforts. This is basically the same as the guy above (and before) you told, so I'm marking his as accepted. I actually tried that before, but even this has caveats. See my comment to the guy above you regarding how this breaks if the stripe has any kind of content that is larger than the TextView. In this case, the stripe content will be cut out. Yes, mea culpa for not wording it better, but I can always ask again. – davidcesarino Aug 18 '13 at 21:26
  • I tried it and it doesn't, the only thing that solved it is to put it in LinearLayout – Ronny Shibley Nov 14 '16 at 13:20
1

It's an annoying problem and you need to put the RelativeLayout inside a LinearLayout

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:minHeight="40dp"
        >
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <RelativeLayout
                android:id="@+id/deleteItem"
                android:layout_width="60dp"
                android:layout_height="match_parent"
                android:layout_alignParentRight="true"
                android:clickable="true"
                android:background="@color/background_grey"
                >
                <TextView
                    style="@style/SmallButtonFont"
                    android:layout_marginRight="12dp"
                    android:layout_centerInParent="true"
                    android:text="CLEAR"
                    android:textColor="@color/globalRedLight" />
            </RelativeLayout>
    </RelativeLayout>
</LinearLayout>
Ronny Shibley
  • 2,030
  • 22
  • 25
1

Add listener to list item holder so every time when holder change height, set that height to a child. i1 is for top, and i3 is for bottom.

parent.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5, int i6, int i7) {
            child.setTop(i1);
            child.setBottom(i3);
        }
    });
Bugs
  • 4,491
  • 9
  • 32
  • 41
C. Alen
  • 184
  • 1
  • 10