Search code examples
androidandroid-relativelayout

How to align view (like in RelativeLayout) with a nephew view?


Related question. Answer: RelativeLayout can't do it. I'm asking how to do it anyway, with not just RL, or with something else.

General story: you have a complex layout that would be difficult to adjust, and along comes a request for something to be added, aligning with a nested view.
enter image description here

What is the best approach? A popup with a custom style? (not familiar with those yet)? Spending days changing the whole hierarchy to a single RelativeLayout? A custom Layout class as wrapper?
AbsoluteLayout (deprecated) or FrameLayout with programmatically changed LayoutParams or margins? (this I'd rather avoid, I prefer not to touch onMeasure, etc)

Simplified example (no relation to pic above):
LinearLayout defines relative heights of the elements. I don't know to do it with RelativeLayout.
anExpandableView is something to be animated as sliding from under someBar (here; full-width, but perhaps it may need to align its width, as well as vertical position).

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <include
            android:id="@+id/topStuff"
            layout="@layout/incl_topstuff"
            android:layout_width="match_parent"
            android:layout_weight="7"
            android:layout_height="0dip" />

        <include
            android:id="@+id/someBar"
            layout="@layout/incl_filters_and_stuff"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <include
            android:id="@+id/bottomStuff"
            layout="@layout/incl_bottomstuff"
            android:layout_width="match_parent"
            android:layout_height="0dip"
            android:layout_weight="10" />

    </LinearLayout>

    <include
        android:id="@+id/anExpandableView"
        layout="@layout/incl_filters"
        android:visibility="gone"
        android:layout_below="@id/someBar"/>

</RelativeLayout>

I know SO has an aversion to general questions, but I don't want an ad-hoc solution. I am asking what to do in cases which would be solved if only a wrapping RelativeLayout would allow alignment to a view that is not a direct sibling.


Solution

  • Putting it simply, RelativeLayout can only measure and layout it's direct children based on each other, but I guess you already knew that.

    The only general solution would be to implement your own custom Layout class, which I wouldn't recommend. If I had to guess why RelativeLayout does not traverse the entire layout hierarchy at it's level and below, it's probably for performance reasons.

    Unfortunately if you're using RelativeLayouts and LinearLayouts and you want views to be dependent on each other you have to pick one approach and stick to it, either the flat hierarchy of RelativeLayout, or the nested one of LinearLayout.

    Based on your example, as far as I know, there is no way to implement weighted views with a RelativeLayout, so you're stuck with using a LinearLayout.

    The easiest way to do what you want is to inflate your expandableView in code, align it with the bottom of the RelativeLayout, set it's height and position based on bottomStuff, and animate from there.

    If you really want to do it in xml, I can think of one somewhat hacky, ad-hoc approach, but which can can be generalized to mirroring the measurement and layout of any hierarchy with a bit of work.

    Create a parallel but invisible LinearLayout that is a sibling of the first one. Give it an empty view with weight 7 on top, an invisible copy of someBar in the middle, then your expandable view under that with weight 10. To have it slide up, either animate the height of the invisible someBar and the weight of the empty view on top towards 0, or remove them/set them to gone and set animateLayoutChanges on your LinearLayout.