How do I apply mathematical operations to Android dimensions?

How do I avoid this hardcoded math...

<resources>
 <dimen name="uno">10dip</dimen>
 <dimen name="dos">6dip</dimen>
 <dimen name="uno_plus_dos">16dip</dimen>
</resources>

<Button 
 android:layout_marginTop="@dimen/uno_plus_dos" />

...and covert it to something like this?

<Button
 android:layout_marginTop="@dimin/uno + @dimen/dos" />

Solution 1:

You don't, sorry. Layout XML files do not support expressions. You either:

  • Leave it as @dimen/uno_plus_dos, or
  • Set your margins in Java code, where you can replace a single resource with a bunch of extra lines of code, or
  • Write your own layout preprocessor that handles expressions like this

UPDATE The data binding library supports some operations in its expressions. I am uncertain if it can handle this specific scenario.

Solution 2:

One trick for simple addition is to use margin + padding.

Solution 3:

Using databinding:

android:layout_marginTop="@{@dimen/uno + @dimen/dos}"

IFAIK margins adapters are not provided by the sdk. You will need to define it yourself:

@BindingAdapter("android:layout_marginTop")
public static void setBottomMargin(View view, int bottomMargin) {
    ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
    layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin,
            layoutParams.rightMargin, bottomMargin);
    view.setLayoutParams(layoutParams);
}

Make sure databinding is enabled for your project :

dataBinding {
    enabled = true
}

in your build.gradle.

The databinding doc is worth reading.

Solution 4:

There's a few tricks around this, but it wouldn't be as nice as your proposed solution, which is something I want as well. For example, you can use layout paddings on not just the View (Button in this case), but you can also do it on the view's parent (the layout like LinearLayout/RelativeLayout). You can also put in invisibile Views (a straight View object works often) with fixed dimensions. It would be like

<View
   android:layout_width="1px"
   andoird:layout_height="@dimen/dos" />

Note that 1px is fine if want to guarantee only 1 pixel will be drawn for a dimension, which is usually what you want if you want use empty views for padding. Some say FrameLayout is better to use for empty padding, but that's descended from View

Sometimes you can combine padding and the layout padding, but that can get messy and have your view cropped. Also you can have something like a FrameLayout or a LinearLayout contain just that view, and use that to have the added padding