Anchors in RelativeLayouts

Android’s layout system can handle most layouts with no problem at all, but for some layouts it is simply not up to the task, even if the layout can sometimes look quite simple. But of course, we can always, or, at least in this case, fix it using our own view.

The Problem

Imagine you get this sketch from your designer.

Anchor Sketch

There are two things that are important. The first one is that the left view should be exactly two thirds of the width of the item, and the one to the right one third. This is needed in order to align with other elements in the UI. The second thing is that the little marker-view should overlap the border of the two views. Seems simple, right? So, how do we solve it?

A solution?

Well, the two thirds part is no problem, a LinearLayout with weights easily solves that part. But now how do we position the marker-view. Well, we can do that with a RelativeLayout quite easily. So let’s put our LinearLayout in a RelativeLayout and then the marker view as another child to the RelativeLayout and position it. Turns out that the RelativeLayout can’t position our marker-view were we want it since the RelativeLayout only knows about the LinearLayout and not about it’s children.

Ok, let’s try a new solution. Let’s put both the right and the left view and the marker as children to the same RelativeLayout. Now it’s no problem to position the marker-view correctly. However, now it’s not possible to distribute the width of the left and right view correctly. Turns out that the RelativeLayout cannot handle relative widths (not that relative if you ask me).

A small custom view to our rescue

One way to solve this is to create what I like to call an anchor view. A view that we can anchor the other views to. Let’s create a new view called AnchorView that extends View and then implement onMeasure() like this:

protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension((int) (0.667f * getMeasuredWidth()), getMeasuredHeight()));

What this does is that it changes the normal width this view would have to 0.667 times that width, or, in other words, two-thirds of that width. So, if we add this view to our xml-layout with the width match_parent, then the above code will modify this width to two thirds of the width. The AnchorView itself will be invisible to users since it doesn’t draw anything. Then we can use the AnchorView to anchor the left and right views like this:



        android:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras a erat sed sapien vehicula tempor. Nam gravida tempor mi. "

        android:text="This is a view"



In the above xml, I’ve added the relative widths as custom attributes to the view to make the xml a bit more readable and also added an option to set the relative height. This makes it more general and usable in other places as well. Take a look at this blog post if you’d like to know how to add custom attributes to views.

The result looks like this:

Anchor Layout

It doesn’t look much, but when you really need it, it’s good to know that there is a way around some of the not-so-good parts of androids layouting system. If you ever wanted to layout things using percentage a bit more flexibly than using weights on LinearLayouts, this is a quite simple way to solve it that keeps the view hierarchy flat. Below is the full code for the AnchorView with the custom attributes.

public class AnchorView extends View {

    private final float mRelativeWidth;
    private final float mRelativeHeight;

    public AnchorView(final Context context, final AttributeSet attrs) {
        super(context, attrs);

        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.AnchorView);
        mRelativeWidth = clamp(attributes.getFloat(R.styleable.AnchorView_relativeWidth, 1));
        mRelativeHeight = clamp(attributes.getFloat(R.styleable.AnchorView_relativeHeight, 1));

    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension((int) (mRelativeWidth * getMeasuredWidth()), (int) (mRelativeHeight * getMeasuredHeight()));

    private float clamp(float value) {
        if (value > 1) {return 1;}
        if (value < 0) {return 0;}
        return value;

If you’re interested you can find all the code for this at github.

This Post Has 4 Comments

  1. Himanshu Bansal

    You can still make this layout using LinearLayout, assign weights to two child views (2/3, 1/3), and give a margin_left = 0.5dp to the right-view.

    Also, put the bacground of the LinearLayout, same as that of the marker.

    In this way, you have actually not added any marker, but the layout renders the exact same way, with only a minor 0.5dp taken away from the right view. I think thats better than adding another node (AnchorView) in the hierarchy.

    1. Anders Ericsson

      I don’t think your suggestion solves the same problem as I am trying to solve since your suggestion removes the marker. I have to have a marker view (since it would contain an image and a text) and I also don’t want to be dependent on background colors for it to render correctly. Both the right and left views (and the LinearLayout) should be able to be transparent.

    2. Sameer Vasantgadkar

      Also setting background to complete viewgroup may result in overdraw.

  2. Vinod

    Nice learning for me!!!!!

Leave a Reply