Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android material aligning font baseline to 4dp grid

The Android Material Design Specification says "Type aligns to a 4 dp baseline grid."(http://www.google.com/design/spec/layout/metrics-and-keylines.html#)

It also says in the example graphics that a row in a list should be 72dp high.

It also indicates different font styles and what size the font should be and what its leading should be (http://www.google.com/design/spec/style/typography.html#typography-standard-styles).

I am trying to apply all of these rules to my application to adjust the layout of a row in my listing however I do not know how to space out the text boxes in my row.

I have 3 lines of text to display in each of my row

Heading line one - size 14sp Details line two - size 12sp Details line three - size 12sp

How do I ensure that the baseline of the text in each of these text boxes align with the grid? I can not align the bottom of the text box as this is not the base line, the bottom of the text box is the baseline + descent isn't it?

I need to space out the 3 lines of text as evenly as possible but ensure their baselines align to the grid.

I don't believe I can simply use padding/margins as this will not ensure the baselines of each of the lines of text align with the grid.

Futhermore when I do all this calculations I need to ensure that the row height is 72dp (for an mdpi device with normal font size specified).

Finally how would i specify the leading? As from what I understand this is the space from the baseline of the text of the row above to the top of the highest text in the bottom row. Again i cant use padding/margin as this is not from the baseline.

enter image description here

Edit: The Material Specification for lists have a little more information on how the Tiles in the list should appear when you have 3 lines of text. http://www.google.co.uk/design/spec/components/lists.html#lists-keylines

But it still does not indicate how the 3 lines of text are actually placed vertically so that their baselines align on the 4dp grid. enter image description here

like image 284
se22as Avatar asked Oct 21 '14 13:10

se22as


3 Answers

Update

Based on my answer I made a small library. It is better tailored for real world use than this answer, so please feel free to check it out.

Instead of simply ensuring 4dp alignment, it allows to set leading on all lines and the first line separately, and the last line descent:

enter image description here


Previous reply, left for historical purposes (large screenshot removed)

OK, this is pretty late, but I figured out how to do it in the least possible hackish way.

This method will land an existing view on a baseline grid, given that its upper bound is already on the baseline grid. Input parameters are the step of the grid (4dp in pixels, probably read from resources) and desired leading of the line (e.g. 20dp in pixels).

Note that this won't get you the exact heights that you need, but it will make TextView's size deterministic (i.e. height multiple of step with baseline being on the grid), making it much easier to layout as you need with the use of integer margins.

private static void setLeading(TextView view, int step, int leading) {
    // This is to make the behavior more deterministic: remove extra top/bottom padding
    view.setIncludeFontPadding(false);

    // Get font metrics and calculate required inter-line extra
    Paint.FontMetricsInt metrics = view.getPaint().getFontMetricsInt();
    final int extra = leading - metrics.descent + metrics.ascent;
    view.setLineSpacing(extra, 1);

    // Determine minimum required top extra so that the view lands on the grid
    final int alignTopExtra = (step + metrics.ascent % step) % step;
    // Determine minimum required bottom extra so that view bounds are aligned with the grid
    final int alignBottomExtra = (step - metrics.descent % step) % step;

    view.setPadding(view.getPaddingLeft(), view.getPaddingTop() + alignTopExtra, view.getPaddingRight(), view.getPaddingBottom() + alignBottomExtra);
}
like image 145
Actine Avatar answered Nov 15 '22 13:11

Actine


You can use a helper view of a specific height to baseline the textviews to this height.

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

    <com.example.baselinetest.BaseLineHelperView
    android:id="@+id/v_baselinehelper"
    android:layout_width="match_parent"
    android:layout_height="30dp"
    android:background="#FF0000" />

    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignBaseline="@id/v_baselinehelper"
    android:text="@string/hello_world" />

    </RelativeLayout>

Use this helper view

public class BaseLineHelperView extends View {

   @TargetApi(Build.VERSION_CODES.LOLLIPOP)
   public BaseLineHelperView(Context context, AttributeSet attrs, int  defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);
   }

   public BaseLineHelperView(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
   }

   public BaseLineHelperView(Context context, AttributeSet attrs) {
     super(context, attrs);
   }

   public BaseLineHelperView(Context context) {
     super(context);
   }

   @Override
   @ExportedProperty(category = "layout")
   public int getBaseline() {
     return getHeight();
   }
}
like image 30
mobo Avatar answered Nov 15 '22 13:11

mobo


You should refer to this class in order to align text in the TextView to 4dp baseline grid:
https://github.com/nickbutcher/plaid/blob/master/app/src/main/java/io/plaidapp/ui/widget/BaselineGridTextView.java

like image 36
Nikita Barishok Avatar answered Nov 15 '22 14:11

Nikita Barishok